In Part 1 of our journey into decentralized app development with ink!athon, we explored the decentralized foundational principles of a blockchain at the CTRL+Hack+ZK hackathon. From the vibrant coding symphony workshop to a quick review of decentralized front-end and back-end components, Dennis Zoma, Co-founder of Scio Labs, laid the groundwork for building robust full-stack dApps. Now, as we venture into Part 2, our focus shifts to a deeper exploration of the boilerplate – the ink!athon stack itself!
The Stack of ink!athon - An Overview
The ink!athon stack is capsuled in a monolithic repository (Monorepo) structure. The key characteristic is to house multiple related projects or components (in this case, the front-end and smart contract) under a single repository, facilitating easier code sharing and centralized management.
According to Dennis, PNPM or Yarn may serve as a package manager, although he clearly stated he preferred the former over the latter as it works better in a Monorepo in his experience. PNPM is a package manager for JavaScript, standing for "Plug'n'Play Package Manager." Package managers and Monorepos serve distinct purposes in software development, but there can be interactions between them, especially in the context of managing dependencies in larger projects.
Think of a package manager as a single tree in a forest, where each tree represents an individual project. Each tree has its own set of roots (dependencies) that it relies on. On the other hand, a Monorepo is like an interconnected ecosystem where multiple plants (projects) share the same soil (repository) and can benefit from common resources (shared code and dependencies).
Dependencies, on the other hand, refer to external libraries, modules, or packages that a project relies on to function properly. These external components are very essential for providing reusable code, additional functionalities, or specific tools. Dependencies are typically managed by a package manager and are specified in a project's configuration. For example, in a Rust project using Cargo as the package manager, dependencies are declared in the Cargo.toml file. When you execute cargo build, it fetches and builds the specified dependencies, enabling your project to incorporate code from those external
sources.
How package managers and Monorepos relate
Dependency Management: Package managers like npm (node package manager) for JavaScript or Cargo for Rust focus on individual project dependency management. In contrast, Monorepos allows multiple projects to coexist in a single repository, streamlining efficient dependency sharing across projects.
Code Sharing: Package managers facilitate sharing and distributing code or libraries within a broader community. Monorepos, on the other hand, promotes code sharing among projects within the repository, enabling the sharing of dependencies between different projects for improved code reuse and maintenance.
Centralized Configuration: Package managers streamline code or library sharing within a broader community, while Monorepos encourages sharing dependencies between projects within a repository, enhancing code reuse and maintenance.
Development Workflow: Package Managers concentrate on the workflow of individual projects, while Monorepos facilitate a collective development workflow, simplifying the coordination of changes across interconnected projects.
While package managers are primarily concerned with individual project dependency management, Monorepos provides a structural and efficient approach to managing dependencies and code sharing across multiple projects within a unified repository. Additionally, having Dockerfiles and deployment configurations in a Monorepo can simplify the process of managing and deploying multiple services.
Key characteristics of a Monorepo
Centralized Management: Changes to any part of the system can be managed within the same repository, simplifying collaboration, version control, and code sharing.
Atomic Changes: Developers can make atomic changes across multiple projects simultaneously, ensuring that changes to one component do not break others. An atomic change ensures that a set of modifications to code or components is treated as a single, cohesive operation. This is beneficial for consistency and reduces the risk of introducing errors by ensuring that all related changes are applied together, minimizing the chances of breaking other parts of the system.
Shared Dependencies: Dependencies, such as libraries and tools, can be shared among projects, reducing duplication and making it more straightforward to maintain and update common components.
Simplified Build and CI/CD Processes: Building, testing, and deploying the entire system or subsets of it can be streamlined in a Monorepo, simplifying continuous integration and continuous deployment (CI/CD) processes.
Ease of Refactoring: Refactoring becomes more straightforward as all related code is in one place, allowing developers to make consistent changes across the entire codebase.
The Stack of ink!athon - The Contract Side
What you have in the Monorepo are two folders: one for the front-end code and another for contracts in one repository. In the Monorepo structure, the /contract directory contains the necessary components for Ink! smart contract development:
Rust Language: The Rust programming language is available in the /contract directory. This is where you would find the Rust source code files for your smart contracts.
Cargo Package Manager: Cargo, which is Rust's package manager, is also present in the `/contract` directory. Cargo is used to manage dependencies, build projects, and handle various aspects of Rust project management.
ink!: The ink! smart contract programming language is part of the setup in the /contract directory. It is specifically designed for writing smart contracts on the Polkadot and Substrate blockchain platforms.
Having these components within the /contract directory allows for a focused and organized structure, making it easier to manage and develop ink! smart contracts within the overall Monorepo.
There are also convenient and efficient scripts available for quickly setting up various aspects of the Substrate contracts development environment:
Substrate Contracts: There's a shorthand script for swiftly initiating Substrate contracts.
Contracts UI: A shorthand script is available for launching the user interface (UI) related to the contracts.
Polkadot.js/apps: There's a shorthand script for spinning up Polkadot.js/apps, a tool for interacting with Substrate-based blockchains.
In the /contract directory, there are preexisting, pre-deployed sample contracts, which are also pre-connected to the front end, although there is also an option to build and deploy them locally. This flexibility suggests that developers have the choice to use the existing deployed version or to customize and deploy their instances of the smart contract.
Overall, the provided shorthand scripts and pre-configurations aim to streamline the process of setting up, deploying, and interacting with Substrate contracts and associated components.
The Stack of ink!athon - The Front-end Side
In the front-end, there's a Next.js application, which is essentially a React application. However, the unique aspect is that it doesn't extensively utilize Next.js API routes. Instead, it is compatible with both the traditional pages directory structure and the newer app directory structure introduced in Next.js.
Pages Directory: The pages directory in the front-end would serve as a building block for distinct parts of the smart contract interaction. Each file within the pages directory corresponds to a specific route or functionality in the dApp, and these routes typically involve interactions with the underlying smart contract. For example, you might have pages representing different aspects of your dApp, such as viewing contract details, interacting with specific smart contract functions (such as updating a user's balance, transferring tokens, or modifying contract settings), or displaying information related to blockchain transactions. The pages directory in this case helps organize the frontend codebase and aligns with different functionalities associated with the smart contract within the dApp.
App Directory Structure: The app directory structure is a newer way of organizing the code in the main branch of a smart contract project. Instead of having all the code for the application in a single directory, it's now organized into an app directory. This makes the code more modular and easier to manage, allowing for better scalability as the project grows.
The dApp boilerplate offers a foundation with basic styling and pre-components for the user interface, allowing developers flexibility in choosing their preferred styling approach, whether it's conventional CSS, utility-first frameworks like Tailwind CSS, styled-components, or any UI framework. According to Dennis Zoma, the Co-founder of AZERO.ID, the intention is to remain as unopinionated as possible, empowering developers to make styling decisions based on personal preferences. This adaptability is particularly valuable in a hackathon scenario, where developers may opt for different styling tools to efficiently prototype and build their decentralized applications.
Additionally, in ink!athon, Polkadot.js serves as a foundational layer for frontend functionality. It is utilized as a base layer to interact with the Polkadot and Substrate blockchain. On top of this, the dApp leverages the ink!athon hook library, which acts as an abstraction layer, simplifying and streamlining the interaction with smart contracts. The ink!athon hook library provides essential functionality, such as managing contract instances and ensuring access to these instances across the entire project. Essentially, it removes away complexities, making it easier for developers to work with smart contracts, access their instances, and integrate blockchain functionality seamlessly throughout the entire project. This abstraction contributes to a more efficient and organized development process for decentralized applications.
Conclusion
ink!athon is a game-changer as it significantly reduces the amount of code you need to write. The difference between a lengthy function using Polkadot.js and a concise one using ink!athon is like turning pages of code into just a few lines. This efficiency isn't a one-time occurrence; it repeats across the entire project. If you have multiple components interacting with the blockchain, handling queries, and executing transactions, ink!athon pays off remarkably. With ink!athon, you import your contracts, metadata, and addresses just once, making them accessible everywhere using a hook. It provides shortcuts for many common tasks and offers constants for various chains and orders, streamlining the development process. Furthermore, it is battle-tested in production by AZERO.ID and hundreds of GitHub dependents.
Comments