Developing smart contracts with ink!

yangwao

Matej Yangwao

Posted on December 16, 2020

Developing smart contracts with ink!

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.
Token Allocation per Project

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 AccountIdBalancesHash, 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

Enter fullscreen mode Exit fullscreen mode

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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. Putting your code on the blockchain
  2. 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.

Edgeware Builders Guild

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://docs.edgewa.re

https://contracts.edgewa.re

https://github.com/edgeware-builders/construction-projects

https://substrate.dev/docs/en/knowledgebase/smart-contracts

https://linktr.ee/edg_developers

💖 💪 🙅 🚩
yangwao
Matej Yangwao

Posted on December 16, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related