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 pre-configured environment can be utilized by installing the Cirrus Core (Private Net) wallet.
This small local network uses a Proof-of-Authority (PoA) consensus algorithm. This network runs completely standalone.
A significant amount of focus has been put into simplifying the 'setup' process and to ensure a consistent development environment for those looking to embark on Smart Contract Development in C#.
A tutorial is available on the Stratis Academy that will help you get started.
There is also a GUI that can be used to aid the development process; a separate operating guide can be found here.
The Stratis smart contract platform runs smart contracts written in the C# language. Contracts run natively on the .NET Core CLR. There are significant 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 below:
The recommended way to deploy and interact with your contracts is by using the Private Net wallet. This simplifies the process of creating and broadcasting transactions to the blockchain.
You may also wish to interact with your contract via the API. This approach is discussed more below:
Differences with regular C#
Smart contracts must execute deterministically (within a public environment), which means that they must always produce the same result every time they run.
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 impossible to instantiate new classes, use fields, catch exceptions, or use static constructors.
Additional determinism and format validation requirements are described here:
The above is certainly the case for developing Blockchain solutions within a decentralized public blockchain. However, permissioned and private blockchain solutions utilize alternate consensus algorithms allowing for more predictable behaviour. These environments do not require the same level of caution when it comes to ensuring determinism.
UTXO-based blockchain, Accounts-based contract interactions
To simplify interactions with contracts, Stratis adds an account abstraction layer and modified wallet behaviour.
The net result is that P2PKH addresses can behave as accounts. You can read more about the account abstraction layer here:
Interacting with contracts using the Stratis 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 and querying the contract state. Available endpoints can be seen here:
Additionally, a public API for limited testing is available here:
In the Private Net of Cirrus Core, we have modified our node to dynamically generate an API endpoint for each contract deployed on the network.
You'll find this referenced on the Stratis Academy within Smart Contract Tutorial - A "Hello World" smart contract:
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 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.
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 and Resources
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.
- Full Node: https://github.com/stratisproject/StratisBitcoinFullNode
- Unity SDK: https://github.com/stratisproject/Unity3dIntegration
- JS Library: https://github.com/stratisproject/bitcore-lib-stratis
- Python Library: https://github.com/stratisproject/pyStratis
- Cirrus Core "Private Net" Edition: https://github.com/stratisproject/CirrusCore/releases/tag/188.8.131.52-privatenet
Smart Contracts Examples
- Official samples repo: https://github.com/stratisproject/StratisSmartContractsSamples
- Whitelisted Cirrus contracts: https://github.com/stratisproject/CirrusSmartContracts
- Community Solution Examples: https://github.com/stratisproject/StratisSmartContractsSamples/tree/master/src/Solutions