Introducing Substrate Smart Contracts with Ink

macro is wrapping its contents with more syntax.

// Ink// events!contract { // rest of contract}// Solidityinterface ContractName { // events // rest of contract}With Ink, our contract variables are written in a struct of the name of our contract.

Hash maps derived from Rust’s HashMap type are in place of Solidity’s mapping type, providing key => value lists.

How Substrate stores valuesAny piece of data persisted on a Substrate chain is called an extrinsic, and Ink provides us the means to store extrinsics on-chain via the storage module, that lives within the core module of the language.

In other words, all contract variables that you plan to persist on chain will use a type from storage.

Conversely, the memory module is also available for data structures to operate on memory.

Solidity on the other hand adopts a different approach to this.

From Solidity 0.

5, storage and memory reference types were introduced to function arguments or function variables, so the contract knows where to reference those variables.

However, this is not necessary for contract variables in Solidity.

Primitive types are also available and consistent throughout both languages; where Rust uses u64, Solidity adopts a more verbose uint64 type declaration.

Overall it is quite simple to achieve the same contract variable structure between the two languages.

// Inkstruct NFToken { AccountID: owner: storage::Value<AccountId>, approvals: storage::HashMap<u64, AccountId>,}// Solidityaddress owner;mapping (uint64 => address) approvals;In the above example, the type of values that storage objects handle are passed in via the angled brackets in place of the type’s generics.

The concept of an initialisation function is present within both Ink and Solidity, albeit implemented in different ways.

With Ink, we explicitly define a deploy() method within a Deploy{} implementation block.

The parameters we define for this method are representative of what data we send when initialising the contract.



for our non-fungible token, we will provide an initial amount of tokens to be minted:// Inks initialisation function, deploy()impl Deploy for NFToken { fn deploy(&mut self, init_value: u64) { .

} }Public and private methods are also defined within impl blocks, where public methods are explicitly defined with pub(external).

Again, when comparing this syntax to that of Solidity’s, internal and external are used to define a private or public resource.

Note: In Rust, functions, modules, traits and structs are private by default, and must be defined with the pub keyword in order for them to be externally reachable.

The (external) extension to pub here is Ink specific, and is compulsory to include with public Ink functions.

// public functionsimpl NFToken { pub(external) fn total_minted(&self) -> Balance {}}// public functionsimpl NFToken { fn mint_impl( &mut self, receiver: AccountId, value: u64) -> bool { }}Again, we have separated our private and public functions in separate impl blocks, and have included pub(external) for public functions defined.

As the last building block of our contract, a tests module is defined that asserts various conditions as our functions are tested.

Within the tests module, we can test our contract logic without having to compile and deploy it to a Substrate chain, allowing speedy ironing out of bugs and verification that the contract works as expected.

Up NextWe have now talked through the building blocks of an Ink smart contract.

In the next part, we will explore the functions of the non-fungible token contract in more depth, understanding the Rust design patterns and how they tie in with our contract logic.

We will then walk through the process of building and deploying the smart contract to a local Substrate chain and test its functions using Polkadot JS.

The link to the next part will be embedded here once it is published.


. More details

Leave a Reply