First Things First

Before embarking on developing a Smart Contract on the Stratis Platform, you will need to have a local environment which you can test and deploy on, to simplify this, a bootstrapped environment can be utilized by installing the Cirrus Core (Hackathon Edition) wallet.

This small local network uses a Proof-of-Authority (PoA) consensus algorithm. In fact, the network can run completely standalone, however, more nodes can join the network by simply opening another instance of Cirrus Core (Hackathon Edition). This network is not a sidechain, the node(s) do not run a sidechain gateway, which means the token created is not pegged to the STRAT and carries no value at all in the real world.

A significant amount of focus has been put into simplifying the ‘setup’ process and ensure a consistent development environment for those looking to embark on Smart Contract Development in C# on the Stratis Platform.

A tutorial is available on the Stratis Academy that will help you get started with the Cirrus Core (Hackathon Edition) wallet: http://academy.stratisplatform.com/SmartContracts/Tutorial1-LocalSidechain/local-network-tutorial-introduction.html#

Overview

The Stratis smart contract platform runs contracts written in the C# language. Contracts run natively on dotnet Core CLR. There are significant and important differences between writing a C# smart contract and writing regular C# code that developers should be aware of.

Working with Smart Contracts

Writing and deploying a contract involves several different tools and processes. Before a contract can be deployed, it must be validated to ensure it conforms to determinism and format requirements. Afterwards, it can be compiled to bytecode and deployed on chain. Both these steps can be performed using the Smart Contract Tool (SCT), available at https://github.com/stratisproject/Stratis.SmartContracts.Tools.Sct/ and included in the Hackathon wallet distribution in the SDK folder.

The recommended way to deploy and interact with your contracts is by using the Hackathon wallet. This simplifies the process of creating and broadcasting transactions to the blockchain. You may also wish to interact with your contract via an API. This approach is discussed more below.

Read more about working with contracts here: https://academy.stratisplatform.com/SmartContracts/Handbook/working-with-contracts.html

Differences with regular C#

Smart contracts must execute deterministically, which means that every time they run they must always produce the same result. The subset of C# that can be used has necessarily been limited. This means that no external libraries and very few core classes are available to developers. Floating point arithmetic is not supported. It’s not possible to instantiate new classes, use fields, catch exceptions, or use static constructors. Additional determinism and format validation requirements are described here: https://academy.stratisplatform.com/SmartContracts/Handbook/contracts-in-depth.html

Metered Execution

Smart contracts must execute deterministically, which means that every time they run they must always produce the same result. The subset of C# that can be used has necessarily been limited. This means that no external libraries and very few core classes are available to developers. Floating point arithmetic is not supported. It’s not possible to instantiate new classes, use fields, catch exceptions, or use static constructors. Additional determinism and format validation requirements are described here: https://academy.stratisplatform.com/SmartContracts/Handbook/contracts-in-depth.html

UTXO-based blockchain, Accounts-based contract interactions

One way the Stratis platform differs from e.g. Ethereum is that it is built on a UTXO-based blockchain. To simplify interactions with contracts, Stratis adds an account abstraction layer and modified wallet behavior. The net result is that P2PKH addresses can behave as accounts. You can read more about the account abstraction layer here: https://academy.stratisplatform.com/SmartContracts/Handbook/account-abstraction-layer.html

Interacting with contracts using the full node API

Contracts can be interacted with via the HTTP API running on a node. Developers building applications on top of a smart contract will need to use this functionality to integrate their apps with their contracts.

The API exposes methods for creating and broadcasting smart contract transactions to the network, as well as querying contract state.

For the hackathon, we’ve modified the full node to dynamically generate an API endpoint for each contract deployed on the network. If you wish, you can use these endpoints within your application as well.

You'll find this referenced on the Stratis Academy within Smart Contract Tutorial 2 - A “Hello World” smart contract: https://academy.stratisplatform.com/SmartContracts/Tutorial2-HelloWorld/hello-world-tutorial-introduction.html

Logs and Receipts

Any time a smart contract transaction is included in a block, nodes will generate a receipt that gives some insight into what happened when that transaction was executed. This should generally be the first thing you check after sending a smart contract transaction.

The receipt includes the following information:

  • The hash of the transaction.

  • The hash of the block the transaction was included in.

  • The height of the block the transaction was included in.

  • Whether the transaction was successfully executed.

  • The amount of gas that was consumed in executing the transaction.

  • The sender of the transaction.

  • The contract address that received a method call, OR

  • The new contract address created by the transaction, assuming it was successful.

  • An error message if the execution failed.

  • The return value if execution was successful.

  • Logs generated as part of execution.

You can access the receipt of a transaction via the wallet (See Iain's docs) or via the Swagger API, using the `/api/SmartContracts/receipt` endpoint with the transaction hash. It's worth noting this will only be available after the transaction has been included in a block so you generally will need to wait about 10 seconds before you can query this.


Logs

You can think of logs within contracts as events that you can raise inside the contract. Outside the contract, you can watch for and search through these events.

Logs are defined as a struct inside contract code and can be output via the `SmartContract.Log` method. The struct's properties can be decorated with the `Indexed` attribute, which makes the marked attributes searchable. More on this later.

A good example is the `TransferLog` event inside `StandardToken`. This log is output every time there is a transfer of tokens. If you were to check the receipt of a successful call to a `StandardToken.Transfer` method, it would contain a `TransferLog` event, as well as the address that was sending the funds, the address receiving the funds, and the amount.

From the contract API, you can also use the `/api/SmartContracts/receipt-search` endpoint to search through all of the transfer events that have been raised in that contract and can even filter by the indexed fields! In the case of `TransferLog`, we can find all transfers to or from a certain address, as both the `To` and `From` properties were indexed.  This approach is particularly useful for building interfaces for smart contracts.

Sample Contracts

For examples of contracts written in C#, take a look at the official Stratis Smart Contract Sample repository. Also check out the CirrusSmartContracts repository, which contains community contributions of smart contracts for deployment on the Cirrus sidechain.