Developing smart contracts with ink!
Matej Yangwao
Posted on December 16, 2020
A little about my crypto journey.
Back in early 2011, were having lectures about Bitcoin in our hackerspace Progressbar. Since then, I've gotten more interested in Bitcoin and more broadly blockchain tech. Later on, I followed the development of Ethereum. I was interested in the EVM which was invented with its own programming language called Solidity. Fast-forward, in 2017, Gavin Wood announced a project called Polkadot along with Substrate, a framework for building application-specific chains. But that's just tech, you need soul to bring ecosystem, working around a common, shared goal. In Q2 2019, Edgeware decided to build a smart contract blockchain using Substrate, seeding the community with a lockdrop. In effect, the team exited to the community from day one. Most of the tokens were given to the community, which developed a great culture from the start—the community includes artists, builders, DAOs, DeFi and game developers. That's one thing that got me interested in Edgeware.
Disclaimer, I'm chain-contractor for Edgeware and a member of Edgeware Builders Guild.
Thanks to Edgeware Lockdrop, the chain have distribution among its believers.
Introducing Ink!
Today, I'll be walking through ink!, a Rust-based embedded domain-specific language (eDSL) for writing Wasm smart contracts. I'm a smart contract developer at heart and this is just one piece of smart contract tooling that Edgeware provides.
Because Substrate implements nearly all the code necessary to launch a working blockchain, including libp2p networking and PBFT consensus, Edgeware core developers get to focus on building create tooling for smart contract developers. They're working on C/C++/Rust smart contracts that can be compiled to WebAssembly (WASM). (Oh, you can also run Solidity-based smart contracts for the Ethereum Virtual Machine (EVM). I'll write about that later.) Onto the guts of ink!
ink! Contract Components
ink! should feel familiar to developers who have programmed using other modern smart contract languages. The skeleton of a contract has all of the same components that you might expect:
- Events
- Storage
- Deployment (Constructor) Function
- Public Functions
- Internal functions
In ink!, mutability and visibility are explicitly defined per contract function. In these functions, you gain access to a number of common Substrate types like AccountId
, Balances
, Hash
, etc. Additionally, you gain access to commonly used environment variable like the caller
, and more!
Design
The main goals of ink! are correctness, conciseness, and efficiency.
ink! is designed to be as close to the Rust programming language as possible. The language uses attribute macros to tag standard Rust structures into understandable contract components.
#[ink(...)]
Copy
Because ink! follows Rust standards, tools like rustfmt
and rust-analyzer
already work out of the box.
Overflow Safety
Being written in Rust, ink! can provide compile-time overflow/underflow safety. Using a Rust compiler configuration, you can specify whether you want to support overflowing math, or if you want contract execution to panic when overflows occur. No need to continually import "Safe Math" libraries, although Rust also provides integrated checked, wrapped, and saturated math functions.
Test Environment
ink! provides a built in test environment that can be used to perform off-chain unit testing with the Rust framework. This makes it simple and easy to ensure that your contract code functions as expected, without the need for third party testing platforms.
ink! vs Solidity
Rust is an ideal smart contract language. It is type safe, memory safe, and free of undefined behaviors. It generates small binaries because it doesn’t include extra bloat, like a garbage collector, and advanced optimizations and tree shaking remove dead code. Through compiler flags, Rust can automatically protect against integer overflow.
ink! chooses not to invent a new programming language, but rather adapt a subset of Rust to serve this purpose. As a result, you gain from all of the tooling and support available to the Rust ecosystem for free. In addition, as the language develops, ink! will automatically gain access to new features and functionality, improving how you can write smart contracts in the future.
Here is a brief comparison of features between ink! and Solidity:
Virtual Machine | Any Wasm VM | EVM |
---|---|---|
Encoding | Wasm | EVM Byte Code |
Language | Rust | Standalone |
Overflow Protection | Enabled by default | None |
Constructor Functions | Multiple | Single |
Tooling | Anything that supports Rust | Custom |
Versioning | Semantic | Semantic |
Has Metadata? | Yes | Yes |
Multi-File Project | Planned | Yes |
Storage Entries | Variable | 256 bits |
Supported Types | Docs | Docs |
Has Interfaces? | Planned (Rust Traits) | Yes |
Basic ink! contract - Flipper
The ink CLI automatically generates the source code for the "Flipper" contract, which is about the simplest "smart" contract you can build. You can take a sneak peak as to what will come by looking at the source code here:
#![cfg_attr(not(feature = "std"), no_std)]
use ink_lang as ink;
#[ink::trait_definition]
pub trait Flip {
/// Creates a new flipper smart contract initialized with the given value.
#[ink(constructor)]
fn new(init_value: bool) -> Self;
/// Flips the current value of the Flipper's bool.
#[ink(message)]
fn flip(&mut self);
/// Returns the current value of the Flipper's bool.
#[ink(message)]
fn get(&self) -> bool;
}
#[ink::contract]
pub mod flipper {
use super::Flip;
#[ink(storage)]
pub struct Flipper {
value: bool,
}
impl Flipper {
/// Creates a new flipper smart contract initialized to `false`.
#[ink(constructor)]
pub fn default() -> Self {
Self::new(Default::default())
}
}
impl Flip for Flipper {
#[ink(constructor)]
fn new(init_value: bool) -> Self {
Self { value: init_value }
}
#[ink(message)]
fn flip(&mut self) {
self.value = !self.value;
}
#[ink(message)]
fn get(&self) -> bool {
self.value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_works() {
let flipper = Flipper::default();
assert_eq!(flipper.get(), false);
}
#[test]
fn it_works() {
let mut flipper = Flipper::new(false);
// Can call using universal call syntax using the trait.
assert_eq!(<Flipper as Flip>::get(&flipper), false);
<Flipper as Flip>::flip(&mut flipper);
// Normal call syntax possible to as long as the trait is in scope.
assert_eq!(flipper.get(), true);
}
}
}
The Flipper contract is nothing more than a bool which gets flipped from true to false through the flip() function.
Different Smart Contract Deployment
Smart contract deployment on Edgeware is a little different than on traditional smart contract blockchains.
Whereas a completely new blob of smart contract source code is deployed each time you push a contract on other platforms, Substrate optimizes this behavior. For example, the standard ERC20 token has been deployed to Ethereum thousands of times, sometimes only with changes to the initial configuration (through the Solidity constructor
function). Each of these instances takes up space on the blockchain equivalent to the contract source code size, even though no code was actually changed.
In Edgeware, the contract deployment process is split into two halves:
- Putting your code on the blockchain
- Creating an instance of your contract
With this pattern, contract code like the ERC20 standard can be put on the blockchain a single time, but instantiated any number of times. No need to continually upload the same source code over and waste space on the blockchain.
Treasury Funding - Construction Projects
As part of Edgeware Builders Guild, we want to nurture healthy community builders and give opportunity to thrive through on-chain governance. If you're a builder, your project can be funded in various ways. The main way is through treasury funding through construction projects. Here, we are looking for particular interests like user interfaces, software development, system integration and research. The max amount of funding for technical grants is $100,000. Teams shouldn't seek to cover 100% of their early-stage funding via Edgeware grants alone.
Teams can apply for grants more than once, but they need to complete the previous project (as described in their application) before receiving additional funds. You can learn about process how to apply here. After your grant gets your project off the ground, you can apply to BuildDAO an on-chain incubator. Apply with an idea, get $50k to seed your startup or protocol on Edgeware.
You can review Edgeware Developer Deck, chat with guild members at Telegram and follow recent events among Edgeware community developers
Conclusion
Edgeware offers numerous opportunities to learn new language - ink!, brew your own idea and get initial funding to grow along with Edgeware community builders guild. If you are Solidity developer, it's worth noting that Edgeware supports EVM as well so you can apply with your existing dApp in Solidity for funding too! Come over and if you are curious to ask us questions at chat and let us know what you want to build and we can guide you through process to get funded.
References
https://github.com/edgeware-builders/construction-projects
Posted on December 16, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.