In this article we are going to dive into the basic setup and best practices for using Ruby on Rails alongside Solidity to create a dApp. Here we tackle the installation of Ruby, Ruby on Rails, Ethereum developers’ tools, like Ganache and Truffle, the implementation of the necessary ruby gems to connect to an Ethereum network, and the best practices to integrate both technologies.
Over the last few years, decentralized applications and web pages have gained immense popularity, and with very good reason. The advent of blockchain technology has revolutionized the way people interact, trade, and share information online, offering unparalleled security, transparency, and trust. Decentralized applications, or dApps, leverage the power of blockchain networks like Ethereum, enabling users to interact directly with one another without the need for central authorities or intermediaries. This has led to the emergence of innovative solutions across various industries, such as decentralized finance (DeFi), supply chain management, gaming, content-sharing platforms, and, our topic today, Smart Contracts. As we continue to witness the rapid growth and adoption of dApps, it becomes increasingly clear that they have the potential to reshape the digital landscape and redefine the way we conduct business and interact online.
On the other hand, Ruby on Rails (RoR) is one of the most popular frameworks nowadays. Writing web applications has become a simple, well-documented process with RoR. With a thriving community behind it and new gems (libraries) being developed every day, it stands out as one of the best options for web development.
Before diving into the best practices, let’s review in a nutshell the process of setting up a web application in RoR that will use the Ethereum blockchain.
We won't delve too deeply into the specifics of the installations since there are a ton of tutorials and resources available on the installation topic and setup of Ruby and Ethereum-based apps. However, here are the basics:
Ruby on Rails
Installing Ruby on Rails is quite a straightforward process, but in keeping with one of the core principles of RoR, DRY (Don't Repeat Yourself), we recommend referring to a comprehensive tutorial. The team at GoRails has created an easy-to-follow guide for installing Rails (and Ruby) on any system: Install Ruby On Rails.
P.S., Ruby only runs on MacOS and Linux natively; so, in order to install it on Windows, you must have WSL enabled and a Linux image installed. The tutorial covers these steps as well.
Set up Ethereum development tools
In order to interact with the Ethereum blockchain, we will need two tools: Ganache, which will allow us to simulate a personal Ethereum blockchain for development. And Truffle, a very useful Framework that will allow us to write our contracts.
To use both these tools, you need to have NodeJs installed in your machine. If you followed the GoRails tutorial, you should be good to go. If, for some reason, you want to install NodeJs individually, you can simply download it from the official web (Download Node.js) and install as any other software.
Ganache comes in 2 flavors, UI and command-line. To install the UI version, the process is as simple as going to Ganache - Truffle Suite, selecting your OS versions, and installing normally.
This way, you will have a very nice control center from which you can create and administer your workspaces.
If you need to install the command-line version (to implement on a server, for example), you can use this guide in the README here.
Ganache has recently launched an interactive Documentation page. Here, you can learn and test everything you need to create your SmartContract app.
To install Truffle (to compile and deploy your contracts), you can simply do the following:
After this, you can simply install the Ethereum gem in your Ruby project. Just go to the Gemfile and add gem ethereum.rb then, run bundle install, and that should be it!
First things first, we need to create a new workspace, we can use this command for that:
Now, let’s create a simple contract with Solidity for storing and retrieving a string value, called SimpleStorage.sol
In order to use this contract in Rails, we need the ABI address.
Initialize Truffle project:
Create a new directory for your Truffle project and navigate to it:
Initialize a new Truffle project:
This command will generate the necessary Truffle project files and directories.
Add your Solidity contract:
Copy your SimpleStorage.sol file into the contracts directory within the Truffle project.
Edit the truffle-config.js file to configure the networks you want to deploy to. For example, to connect to your local Ganache instance, add the following configuration:
Replace your twelve-word mnemonic phrase here with the same mnemonic you used when starting Ganache.
Note: You need to install @truffle/hdwallet-provider to use HDWalletProvider in the configuration:
Create a migration script:
Create a new file named 2_deploy_simple_storage.js in the migrations directory with the following content:
Compile the contract:
In your Truffle project directory, run the following command to compile your Solidity contract:
This will generate the contract's ABI (Application Binary Interface) and Bytecode in the build/contracts directory.
Deploy the contract:
In your Truffle project directory, run the following command to deploy your contract to the specified network (in this case, the development network):
After the deployment is successful, you will see the contract's address in the console output, and you’ll be able to use it in your Rails app.
The first thing we need is to connect our Rails app to our Ganache workspace. Doing this is as simple as requiring the Ethereum gem and calling.
Then, we need to create a model class for our contract, using our address and ABI from the compilation before.
And that’s it. Your solidity contract is now usable inside our Rails application.
Now, let’s review what is the best way to implement this, and the Best Practices for Building Smart Contracts!
One of the most important principles in any programming language is DRY (Don’t repeat yourself). One way to do this is by implementing a modular approach. This will allow us to use components we ourselves have created or implement libraries to achieve the best solution.
Another significant advantage of using modular code in smart contract development is that it allows for more straightforward testing and maintainability. As each module can be tested independently, it becomes easier to identify issues and verify the correct functionality of the individual components. Furthermore, modular code simplifies maintenance efforts since changes or bug fixes in one specific module do not necessarily impact the rest of the modules.
To enhance the modularity of smart contracts in Solidity, we can use the import statement to include external contracts or libraries. This functionality enables the creation of reusable components that can be imported and used across various contracts, further promoting modular code and ensuring that smart contracts remain efficient, maintainable, and easy to test. By adopting modular code and separation of concerns in smart contract development, we can create more robust and reliable decentralized applications (dApps) that leverage the full potential of blockchain technology.
The primary benefits of TDD in smart contract development include ensuring the contract behaves as expected and catching potential vulnerabilities before deployment. This is particularly important in blockchain technology, where smart contracts handle valuable assets and sensitive transactions. TDD also encourages modular and maintainable code since each component is developed and tested independently.
Embracing a Test-driven Development approach when building smart contracts leads to more reliable, secure, and maintainable contracts. With the Truffle framework tools and writing thorough tests, we can create dependable smart contracts, minimizing the risk of vulnerabilities and fostering trust in decentralized applications (dApps).
Ruby on Rails, like many other frameworks, implements the Model-View-Controller (MVC) architecture; this incredible schema, when integrating Solidity with Ruby on Rails, ensures a clean separation of presentation, business, and data access logic. The MVC architecture is a design pattern that promotes modularity and maintainability in software applications, making it an ideal choice for combining Solidity.
Within the MVC architecture, Solidity contracts act as models, representing the data structures and business logic of the application. Rails controllers serve as the intermediary layer, responsible for interacting with the Ethereum blockchain and managing the data flow between Solidity contracts and views. And the Views do what they do best, display the information retrieved from the blockchain, presenting data to users in a coherent and visually appealing manner.
Adopting the MVC architecture when integrating Solidity with Ruby on Rails fosters a well-organized and maintainable application structure. By creating Solidity contracts as models, utilizing Rails controllers for blockchain interactions, and developing views for data presentation, we can build efficient and scalable decentralized applications that seamlessly blend the smart contract functionality with the robustness of the Ruby on Rails framework.
The significance of security in smart contract development cannot be overstated, as vulnerabilities can result in substantial financial losses and damage to reputations. The idea behind smart contracts is basically an immutable, trackable, trustable contact. To ensure the security of your smart contracts, consider the following practices:
In Solidity, there are two primary types of data storage: storage and memory. Understanding when and where to use each type is important for optimizing your smart contracts.
Here are some guidelines for using storage and memory:
By choosing the right data storage type for your use case, you can optimize performance and cost efficiency of your smart contracts.
Gas optimization is critical for minimizing the cost of deploying and interacting with your smart contracts. Here are some tips for optimizing gas usage:
a. Use view and pure functions: These functions do not modify the state of the contract and do not consume gas when called externally.
b. Minimize storage use: Storing data on the Ethereum blockchain is expensive. Reduce storage costs by using events, which are logged in the transaction receipts and do not consume as much gas as storage operations.
c. Use efficient data types: Use the most appropriate data type for your variables, as it can affect the gas usage. For example, uint8 is more gas-efficient than uint256 when storing small numbers.
d. Optimize loops: Loops can consume significant gas, especially if they iterate over large data sets. Consider using techniques like pagination or mapping to minimize gas consumption.
Something important to keep in mind, and this is more a must than a recommendation. When creating smart contracts, the functions we create inside these contracts must have a deterministic complexity, not a probabilistic one. So, random for loops or calls to an API, functions that we don’t know how much time they’ll take (how much gas they’ll consume) can not be created inside contracts.
To facilitate communication between Rails applications and smart contracts, follow these best practices:
a. Use the Ethereum.rb gem: This gem provides a simple interface to interact with the Ethereum blockchain, allowing you to call contract functions and deploy new contracts.
b. Use asynchronous calls: Blockchain transactions can take time to be mined and confirmed. Implement asynchronous calls in your Rails application using background jobs (e.g., Sidekiq or Delayed Job) to avoid blocking the main thread and ensure a responsive user experience.
c. Cache contract data: To minimize the number of calls made to the Ethereum network, cache frequently accessed contract data in your Rails application. This can be achieved using Rails caching mechanisms, such as Redis or Memcached.
d. Handle exceptions gracefully: Be prepared to handle exceptions that may occur when interacting with the Ethereum network, such as timeouts, rejections, or out-of-gas errors. Implement retries and fallback mechanisms in your Rails application to handle these scenarios.
Integrating Solidity with Ruby on Rails for building smart contracts presents a potent synergy, allowing us to harness the robustness of the Rails framework and the versatility of Solidity. This powerful combination enables the creation of sophisticated decentralized applications (dApps) that leverage the strengths of both technologies, providing a seamless user experience and enhancing the overall functionality of the blockchain ecosystem.
By adhering to these best practices, we can build secure, scalable, and efficient decentralized applications that are not only reliable but also resilient to potential vulnerabilities. Implementing solid programming techniques such as Test-driven Development (TDD), Model-View-Controller (MVC) architecture, and prioritizing security measures help ensure the smooth operation of the dApps and lay a strong foundation for long-term success.
Furthermore, following these practices not only benefits us, developers, but also, protects the interests of users and stakeholders involved in the blockchain ecosystem. As smart contracts often handle valuable assets and sensitive transactions, ensuring their security and reliability is of the utmost importance. The effective implementation of these practices contributes to building trust and confidence in technology, and us as developers or companies.
We’d love to learn more about your project.
Engagements start at $75,000.