您可以使用 Webpacker 在 Rails 6 应用程序中直接使用 Web3.js 吗?

Posted

技术标签:

【中文标题】您可以使用 Webpacker 在 Rails 6 应用程序中直接使用 Web3.js 吗?【英文标题】:Can you use Web3.js directly in a Rails 6 app using Webpacker? 【发布时间】:2020-05-07 19:14:15 【问题描述】:

如何通过 Rails 6 应用与本地 Truffle Ganache 区块链进行交互?我已经在 vanilla javascript 中制作了 FixedSupplyToken 应用程序的原型,并且一切都按预期工作。接下来,我尝试将其移动到 Rails 6 应用程序中。我对使用 Ruby gem 感到紧张(ethereum.rb - 正在寻找人来接管维护,其他人似乎被遗弃了)。我想知道您是否可以使用 Webpacker 将 web3.js 直接导入 Rails 6? 我创建了一个新的 Rails 6 应用并添加了以下内容:

yarn add web3
yarn add truffle-contract

都安装并显示在 package.json 中:


  "name": "app",
  "private": true,
  "dependencies": 
    "@fortawesome/fontawesome-free": "^5.12.0",
    "@fortawesome/free-brands-svg-icons": "^5.12.0",
    "@fortawesome/free-regular-svg-icons": "^5.12.0",
    "@fortawesome/free-solid-svg-icons": "^5.12.0",
    "@rails/actioncable": "^6.0.0",
    "@rails/actiontext": "^6.0.2-1",
    "@rails/activestorage": "^6.0.0",
    "@rails/ujs": "^6.0.0",
    "@rails/webpacker": "4.2.2",
    "bootstrap": "^4.4.1",
    "bootswatch": "^4.4.1",
    "chart.js": "^2.9.3",
    "chartkick": "^3.2.0",
    "datatables.net-bs4": "^1.10.20",
    "datatables.net-buttons-bs4": "^1.6.1",
    "datatables.net-responsive-bs4": "^2.2.3",
    "datatables.net-select-bs4": "^1.3.1",
    "flatpickr": "^4.6.3",
    "jquery": "^3.4.1",
    "popper.js": "^1.16.0",
    "trix": "^1.0.0",
    "truffle-contract": "^4.0.31",
    "turbolinks": "^5.2.0",
    "vue": "^2.6.11",
    "web3": "^1.2.5-rc.0"
  ,
  "version": "0.1.0",
  "devDependencies": 
    "webpack-dev-server": "^3.10.1"
  

然后我将它添加到 app/javascript/packs (web3/index.js):

// Import libraries we need.
import Web3 from 'web3';
import contract from 'truffle-contract';

// Import our contract artifacts and turn them into usable abstractions.
import token_artifacts from './FixedSupplyToken.json';

// TokenContract is our usable abstraction, which we'll use through the code below.
var TokenContract = contract(token_artifacts);

// The following code is to interact with contracts.
var accounts;
var account;

window.App = 
    start: function() 
        var self = this;
        // Bootstrap the TokenContract abstraction for Use.
        TokenContract.setProvider(web3.currentProvider);

        // Get the initial account balance so it can be displayed.
        web3.eth.getAccounts(function (err, accs) 
            if (err != null) 
                alert("There was an error fetching your accounts.");
                return;
            

            if (accs.length == 0) 
                alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly.");
                return;
            

            accounts = accs;
            account = accounts[0];

        );
    ,

    initManageToken: function() 
        App.updateTokenBalance();
        App.watchTokenEvents();
    ,
    updateTokenBalance: function() 
        var tokenInstance;
        TokenContract.deployed().then(function (instance) 
            tokenInstance = instance;
            return tokenInstance.balanceOf.call(account);
        ).then(function (value) 
            console.log(value);
            var balance_element = document.getElementById("balanceTokenInToken");
            balance_element.innerhtml = value.valueOf();
        ).catch(function (e) 
            console.log(e);
            App.setStatus("Error getting balance; see log.");
        );
    ,
    watchTokenEvents: function() 
        var tokenInstance;
        TokenContract.deployed().then(function (instance) 
            tokenInstance = instance;
            tokenInstance.allEvents(, fromBlock: 0, toBlock: 'latest').watch(function (error, result) 
                var alertbox = document.createElement("div");
                alertbox.setAttribute("class", "alert alert-info  alert-dismissible");
                var closeBtn = document.createElement("button");
                closeBtn.setAttribute("type", "button");
                closeBtn.setAttribute("class", "close");
                closeBtn.setAttribute("data-dismiss", "alert");
                closeBtn.innerHTML = "<span>&times;</span>";
                alertbox.appendChild(closeBtn);

                var eventTitle = document.createElement("div");
                eventTitle.innerHTML = '<strong>New Event: ' + result.event + '</strong>';
                alertbox.appendChild(eventTitle);


                var argsBox = document.createElement("textarea");
                argsBox.setAttribute("class", "form-control");
                argsBox.innerText = JSON.stringify(result.args);
                alertbox.appendChild(argsBox);
                document.getElementById("tokenEvents").appendChild(alertbox);
                //document.getElementById("tokenEvents").innerHTML += '<div class="alert alert-info  alert-dismissible" role="alert"> <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button><div></div><div>Args: '+JSON.stringify(result.args) + '</div></div>';

            );
        ).catch(function (e) 
            console.log(e);
            App.setStatus("Error getting balance; see log.");
        );
    ,

    sendToken: function() 
        var amount = parseInt(document.getElementById("inputAmountSendToken").value);
        var receiver = document.getElementById("inputBeneficiarySendToken").value;

        App.setStatus("Initiating transaction... (please wait)");

        var tokenInstance;
        return TokenContract.deployed().then(function (instance) 
            tokenInstance = instance;
            return tokenInstance.transfer(receiver, amount, from: account);
        ).then(function () 
            App.setStatus("Transaction complete!");
            App.updateTokenBalance();
        ).catch(function (e) 
            console.log(e);
            self.setStatus("Error sending coin; see log.");
        );
    ,

    allowanceToken: function() 
        var self = this;

        var amount = parseInt(document.getElementById("inputAmountAllowanceToken").value);
        var receiver = document.getElementById("inputBeneficiaryAllowanceToken").value;

        this.setStatus("Initiating transaction... (please wait)");

        var tokenInstance;
        return TokenContract.deployed().then(function (instance) 
            tokenInstance = instance;
            return tokenInstance.approve(receiver, amount, from: account);
        ).then(function () 
            self.setStatus("Transaction complete!");
            App.updateTokenBalance();
        ).catch(function (e) 
            console.log(e);
            self.setStatus("Error sending coin; see log.");
        );
    
;

document.addEventListener('load', function() 
    // Checking if Web3 has been injected by the browser (Mist/MetaMask)
    if (typeof web3 !== 'undefined') 
        console.warn("Using web3 detected from external source. If you find that your accounts don't appear or you have 0 MetaCoin, ensure you've configured that source properly. If using MetaMask, see the following link. Feel free to delete this warning. :) http://truffleframework.com/tutorials/truffle-and-metamask")
        // Use Mist/MetaMask's provider
        window.web3 = new Web3(web3.currentProvider);
     else 
        console.warn("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask");
        // fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
        window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545"));
    
);

然后,当我在 Docker 容器中启动 Rails 6 应用程序时,一切似乎都很好,直到 webpack-dev-server 尝试编译资产并抛出此错误:

webpack-dev-server    | ℹ 「wdm」: Compiling...
webpack-dev-server    | ✖ 「wdm」: Hash: 8531025c7b3abcc5d778
webpack-dev-server    | Version: webpack 4.41.5
webpack-dev-server    | Time: 1963ms
webpack-dev-server    | Built at: 01/21/2020 4:46:26 PM
webpack-dev-server    |                                                           Asset      Size       Chunks                         Chunk Names
webpack-dev-server    |                          js/application-8df32eacba8ebdbf7444.js  6.49 MiB  application  [emitted] [immutable]  application
webpack-dev-server    |                      js/application-8df32eacba8ebdbf7444.js.map  6.93 MiB  application  [emitted] [dev]        application
webpack-dev-server    |                                                   manifest.json   1.9 KiB               [emitted]
webpack-dev-server    | media/images/Logo_medlarge-e2a09f85c0c6ed39d81ce70b3bd44e51.png   130 KiB
webpack-dev-server    |      media/images/pdf_icon-c00c8db0462b60773a72302f7b9d456b.png  1.36 KiB
webpack-dev-server    |                       media/webfonts/fa-brands-400-088a34f7.eot   129 KiB
webpack-dev-server    |                       media/webfonts/fa-brands-400-273dc9bf.ttf   129 KiB
webpack-dev-server    |                     media/webfonts/fa-brands-400-822d94f1.woff2  74.2 KiB
webpack-dev-server    |                       media/webfonts/fa-brands-400-d7229311.svg   692 KiB
webpack-dev-server    |                      media/webfonts/fa-brands-400-f4920c94.woff    87 KiB
webpack-dev-server    |                      media/webfonts/fa-regular-400-3ac49cb3.eot  33.6 KiB
webpack-dev-server    |                    media/webfonts/fa-regular-400-9efb8697.woff2  13.3 KiB
webpack-dev-server    |                     media/webfonts/fa-regular-400-a57bcf76.woff  16.4 KiB
webpack-dev-server    |                      media/webfonts/fa-regular-400-d2e53334.svg   141 KiB
webpack-dev-server    |                      media/webfonts/fa-regular-400-ece54318.ttf  33.3 KiB
webpack-dev-server    |                        media/webfonts/fa-solid-900-2aa6edf8.ttf   189 KiB
webpack-dev-server    |                        media/webfonts/fa-solid-900-7a5de9b0.svg   829 KiB
webpack-dev-server    |                        media/webfonts/fa-solid-900-7fb1cdd9.eot   190 KiB
webpack-dev-server    |                       media/webfonts/fa-solid-900-93f28454.woff  96.7 KiB
webpack-dev-server    |                      media/webfonts/fa-solid-900-f6121be5.woff2  74.3 KiB
webpack-dev-server    |
webpack-dev-server    | ERROR in ./app/javascript/web3/index.js 126:22-26
webpack-dev-server    | "export 'default' (imported as 'Web3') was not found in 'web3'
webpack-dev-server    |     at HarmonyImportSpecifierDependency._getErrors (/app/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:109:11)
webpack-dev-server    |     at HarmonyImportSpecifierDependency.getErrors (/app/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:68:16)
webpack-dev-server    |     at Compilation.reportDependencyErrorsAndWarnings (/app/node_modules/webpack/lib/Compilation.js:1463:22)
webpack-dev-server    |     at /app/node_modules/webpack/lib/Compilation.js:1258:10
webpack-dev-server    |     at AsyncSeriesHook.eval [as callAsync] (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
webpack-dev-server    |     at AsyncSeriesHook.lazyCompileHook (/app/node_modules/tapable/lib/Hook.js:154:20)
webpack-dev-server    |     at Compilation.finish (/app/node_modules/webpack/lib/Compilation.js:1253:28)
webpack-dev-server    |     at /app/node_modules/webpack/lib/Compiler.js:672:17
webpack-dev-server    |     at _done (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:9:1)
webpack-dev-server    |     at eval (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:32:22)
webpack-dev-server    |
webpack-dev-server    | ERROR in ./app/javascript/web3/index.js 130:22-26
webpack-dev-server    | "export 'default' (imported as 'Web3') was not found in 'web3'
webpack-dev-server    |     at HarmonyImportSpecifierDependency._getErrors (/app/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:109:11)
webpack-dev-server    |     at HarmonyImportSpecifierDependency.getErrors (/app/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:68:16)
webpack-dev-server    |     at Compilation.reportDependencyErrorsAndWarnings (/app/node_modules/webpack/lib/Compilation.js:1463:22)
webpack-dev-server    |     at /app/node_modules/webpack/lib/Compilation.js:1258:10
webpack-dev-server    |     at AsyncSeriesHook.eval [as callAsync] (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
webpack-dev-server    |     at AsyncSeriesHook.lazyCompileHook (/app/node_modules/tapable/lib/Hook.js:154:20)
webpack-dev-server    |     at Compilation.finish (/app/node_modules/webpack/lib/Compilation.js:1253:28)
webpack-dev-server    |     at /app/node_modules/webpack/lib/Compiler.js:672:17
webpack-dev-server    |     at _done (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:9:1)
webpack-dev-server    |     at eval (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:32:22)
webpack-dev-server    |
webpack-dev-server    | ERROR in ./app/javascript/web3/index.js 130:31-35
webpack-dev-server    | "export 'default' (imported as 'Web3') was not found in 'web3'
webpack-dev-server    |     at HarmonyImportSpecifierDependency._getErrors (/app/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:109:11)
webpack-dev-server    |     at HarmonyImportSpecifierDependency.getErrors (/app/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:68:16)
webpack-dev-server    |     at Compilation.reportDependencyErrorsAndWarnings (/app/node_modules/webpack/lib/Compilation.js:1463:22)
webpack-dev-server    |     at /app/node_modules/webpack/lib/Compilation.js:1258:10
webpack-dev-server    |     at AsyncSeriesHook.eval [as callAsync] (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
webpack-dev-server    |     at AsyncSeriesHook.lazyCompileHook (/app/node_modules/tapable/lib/Hook.js:154:20)
webpack-dev-server    |     at Compilation.finish (/app/node_modules/webpack/lib/Compilation.js:1253:28)
webpack-dev-server    |     at /app/node_modules/webpack/lib/Compiler.js:672:17
webpack-dev-server    |     at _done (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:9:1)
webpack-dev-server    |     at eval (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:32:22)
webpack-dev-server    | ℹ 「wdm」: Failed to compile.

Websearches 没有发现任何可能的解决方案,我也没有找到任何在 Rails 中使用 Webpacker 使用 web3.js 的示例。可能吗;有什么解决方案或例子吗? 谢谢!

【问题讨论】:

【参考方案1】:

认为这是一个命名冲突。您已将自己的 javascript 文件命名为 web3/index.js,Webpack 将其视为 web3;这与您尝试以相同名称导入的节点模块冲突。尝试将您自己的 javascript 文件夹/文件重命名为其他名称。相关问题:https://github.com/webpack/webpack/issues/4817#issuecomment-316119100

【讨论】:

我将 web3 目录重命名为 ethereum,并且 webpack-dev-server 编译时没有任何抱怨。谢谢!

以上是关于您可以使用 Webpacker 在 Rails 6 应用程序中直接使用 Web3.js 吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Rails 6 和 webpacker 中使用 morris.js?

在 JS 模块中使用 Rails-UJS(带有 webpacker 的 Rails 6)

如何通过 Webpacker 使用 Rails 6.1 安装 Alpine JS 3

Rails 6.0.3.4 [Webpacker] 编译失败

Rails + webpacker + vue:“您正在使用仅运行时构建的 Vue,其中模板编译器不可用。”

Rails 5/6:如何在 webpacker 中包含 JS 函数?