Installation and usage of the platform

Development and usage of smart contracts

The definition and general description of how smart contracts work on the Waves Enterprise blockchain platform are provided in the Smart contracts article.

The following are examples of Docker smart contracts and WASM smart contracts development.

Development and usage of Docker smart contracts

Preparation for development

Before you start developing a smart contract, make sure that you have the Docker containerization software package installed on your machine. The principles of working with Docker are described in the official documentation.

Also make sure that the node you are using is configured for smart contract execution . If your node is running in the Mainnet, it is by default configured to install smart contacts from the open repository and has the recommended settings to ensure optimal smart contact execution.

If you are developing a smart contract to run on a private network, deploy your own registry for Docker images and specify its address and credentials on your server in the remote-registries block of the node configuration file. You can specify multiple repositories in this block if you need to define multiple storage locations for different smart contracts. Also you can configure repository authorization in the node configuration file.

You can also load a Docker contract image from a repository not specified in the node configuration file using 103 CreateContract transaction, which initiates the creation of a smart contract. For more information, see Development and installation of a smart contract and description of the 103 transaction.

When working in the Mainnet, the Waves Enterprise open registry is pre-installed in the configuration file.

Smart contract development

Waves Enterprise blockchain platform smart contracts can be developed in any programming language you prefer and implement any algorithms. The finished smart contract code is packaged in a Docker image with used protobuf files.

Examples of Python smart contract code using gRPC API methods to exchange data with a node, as well as a step-by-step guide on how to create the corresponding Docker images are given in the following articles:

You can use JS Сontract SDK Toolkit and Java/Kotlin Сontract SDK Toolkit to develop, test and deploy smart contracts in Waves Enterprise public blockchain networks. These toolkits are described in the following sections:

Uploading a smart contract into a registry

If you work in the Waves Enterprise Mainnet, contact the Waves Enterprise Technical Support team to place your smart contract into the open repository.

When working on a private network, upload the Docker image of the smart contract into your own Docker registry as described below.

Uploading a smart contract into a registry on a private network

1. Start your registry in a container:

docker run -d -p 5000:5000 --name my-registry-container my-registry:2

2. Navigate to the directory containing the smart contract files and the Dockerfile script file with commands for building the image.

3. Build the image of your smart contract:

docker build -t my-contract .

4. Specify the image name and its location address in the repository:

docker image tag my-contract my-registry:5000/my-contract

5. Run the repository container you created:

docker start my-registry-container

6. Upload your smart contract to the repository:

docker push my-registry:5000/my-contract

7. Get information about the smart contract. To do this, display the information about the container:

docker image ls|grep 'my-node:5000/my-contract'

This will give you the ID of the container. Output the information about it with the docker inspect command:

docker inspect my-contract-id

Response example:

{
"Id": "sha256:57c2c2d2643da042ef8dd80010632ffdd11e3d2e3f85c20c31dce838073614dd",
"RepoTags": [
    "wenode:latest"
],
"RepoDigests": [],
"Parent": "sha256:d91d2307057bf3bb5bd9d364f16cd3d7eda3b58edf2686e1944bcc7133f07913",
"Comment": "",
"Created": "2019-10-25T14:15:03.856072509Z",
"Container": "",
"ContainerConfig": {
    "Hostname": "",
    "Domainname": "",
    "User": "",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,

The Id field is the identifier of the Docker image of the smart contract, which is entered in the ImageHash field of the 103 transaction when creating the smart contract.

Installing a smart contract into the blockchain

After uploading the smart contract to the repository, publish the contract on the network using the 103. CreateContract transaction.

To do this, sign the transaction via the blockchain platform client, the sign REST API method or the JavaScript SDK method.

The data returned in the method’s response is fed into transaction 103 when it is published.

Below, you will see the examples of signing and sending a transaction using the sign and broadcast methods. In the examples, the transactions are signed with the key stored in the keystore of the node.

Curl-query to sign transaction 103:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '    { \
      "fee": 100000000, \
      "image": "my-contract:latest", \
      "imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65", \
      "contractName": "my-contract", \
      "sender": "3PudkbvjV1nPj1TkuuRahh4sGdgfr4YAUV2", \
      "password": "", \
      "params": [], \
      "type": 103, \
      "version": 1 \
  }' 'http://my-node:6862/transactions/sign'

The response of the sign method, which is passed to the broadcast method:

{
  "type": 103,
  "id": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky",
  "sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew",
  "senderPublicKey": "3kW7vy6nPC59BXM67n5N56rhhAv38Dws5skqDsjMVT2M",
  "fee": 100000000,
  "timestamp": 1550591678479,
  "proofs": [ "yecRFZm9iBLyDy93bDVaNo1PR5Qkkic7196GAgUt9TNH1cnQphq4yGQQ8Fxj4BYA4TaqYVw5qxtWzGMPQyVeKYv" ],
  "version": 1,
  "image": "my-contract:latest",
  "imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65",
  "contractName": "my-contract",
  "params": [],
  "height": 1619
  }

Curl-response to sign transaction 103:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
{
    "type": 103, \
    "id": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky", \
    "sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew", \
    "senderPublicKey": "3kW7vy6nPC59BXM67n5N56rhhAv38Dws5skqDsjMVT2M", \
    "fee": 500000, \
    "timestamp": 1550591678479, \
    "proofs": [ "yecRFZm9iBLyDy93bDVaNo1PR5Qkkic7196GAgUt9TNH1cnQphq4yGQQ8Fxj4BYA4TaqYVw5qxtWzGMPQyVeKYv" ], \
    "version": 1, \
    "image": "my-contract:latest", \
    "imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65", \
    "contractName": "my-contract", \
    "params": [], \
    "height": 1619 \
}
}' 'http://my-node:6862/transactions/broadcast'

After the 103. CreateContract transaction, which specifies a reference to the smart contract in the repository, is published, i.e., written to a block of the blockchain during a mining round, network users will be able to invoke that smart contract.

Note

If the smart contract code is updated in the future, the contract will need to be republished. To do this, use the 107. UpdateContract Transaction.

Important

The smart contract itself is not placed into the blockchain; the blockchain receives the transaction with the hash of the Docker image in which the smart contract code is packaged. Thus the smart contract Docker image hash is stored on all the nodes of the blockchain, but the smart contract itself is in the Docker registry outside the blockchain network.

Smart contract execution

Once a smart contract is installed in the blockchain, it can be invoked with a 104 CallContract Transaction.

This transaction can also be signed and sent to the blockchain via the blockchain platform client, the sign REST API method or the JavaScript SDK method. When signing a transaction 104, specify the ID of the 103 transaction for the called smart contract in the contractId field (the id field of the sign method response).

Examples of signing and sending a transaction using the sign and broadcast methods using a key stored in the keystore of a node:

Curl-query to sign transaction 104:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
"contractId": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky", \
"fee": 10, \
"sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew", \
"password": "", \
"type": 104, \
"version": 1, \
"params": [ \
    { \
        "type": "integer", \
        "key": "a", \
        "value": 1 \
    } \
] \
}' 'http://my-node:6862/transactions/sign'

The response of the sign method, which is passed to the broadcast method:

{
"type": 104,
"id": "9fBrL2n5TN473g1gNfoZqaAqAsAJCuHRHYxZpLexL3VP",
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58",
"senderPublicKey": "2YvzcVLrqLCqouVrFZynjfotEuPNV9GrdauNpgdWXLsq",
"fee": 10,
"timestamp": 1549365736923,
"proofs": [
    "2q4cTBhDkEDkFxr7iYaHPAv1dzaKo5rDaTxPF5VHryyYTXxTPvN9Wb3YrsDYixKiUPXBnAyXzEcnKPFRCW9xVp4v"
],
"version": 1,
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2",
"params": [
    {
    "key": "a",
    "type": "integer",
    "value": 1
    }
    ]
  }

Curl-query to broadcast the transaction 104:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
"type": 104, \
"id": "9fBrL2n5TN473g1gNfoZqaAqAsAJCuHRHYxZpLexL3VP", \
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58", \
"senderPublicKey": "2YvzcVLrqLCqouVrFZynjfotEuPNV9GrdauNpgdWXLsq", \
"fee": 10, \
"timestamp": 1549365736923, \
"proofs": [ \
    "2q4cTBhDkEDkFxr7iYaHPAv1dzaKo5rDaTxPF5VHryyYTXxTPvN9Wb3YrsDYixKiUPXBnAyXzEcnKPFRCW9xVp4v" \
], \
"version": 1, \
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2", \
"params": [ \
    { \
    "key": "a", \
    "type": "integer", \
    "value": 1 \
    } \
] \
}' 'http://my-node:6862/transactions/broadcast'

Development and usage of WASM smart contracts

This section provides an example of the development of a WASM smart contract using Rust CDK. The Rust CDK is a set of libraries and utilities that provide an eDSL for writing smart contracts in the Rust language.

Preparation for development

To get started, you need to have Rust and Cargo installed on your system.

Cargo-we installation

To install cargo-we, run the following command:

cargo install --git https://github.com/waves-enterprise/we-cdk.git --force

Use –force to install the latest version of the utility.

Creating a project

To create a project, use the cargo we new <NAME> command, for example:

cargo we new flipper

This command will create a flipper folder in your working directory. The following files will be created in the folder:

  • Cargo.toml – a file containing the project metadata needed for the build;

  • lib.rs – the source code file for the contract;

  • .gitignore – a file, that specifies file ignore for git.

An example contract, Flipper, will be created in the lib.rs file.

Building the project

To build the project, run the following command:

cargo we build

WASM smart contract example – Flipper

Flipper is a simple smart contract containing only one bool value. The contract provides a method that changes its value from true to false and vice versa. Below is the code of the contract using CDK.

use we_cdk::*;

// Объявление функции, доступной для вызова.
// Для этого используется ключевое слово - #[action].
// _constructor - обязательный метод, который вызывается при CreateContract Transaction.
#[action]
fn _constructor(init_value: Boolean) {
    // Данная функция устанавливает значение, полученное аргументом функции, по ключу "value".
    set_storage!(boolean :: "value" => init_value);
}

#[action]
fn flip() {
    // Читаем значение по ключу.
    let value: Boolean = get_storage!(boolean :: "value");
    // Записываем значение обратное полученному.
    set_storage!(boolean :: "value" => !value);
}

CDK fundamentals

Types

CDK uses types similar to the types available for storage in the contract state:

  • Integer

  • Boolean

  • Binary

  • String

Called functions

To make a function available for external call, specify the action attribute:

#[action]
fn flip() {
...

Called functions must not return values. By default, all functions are not accessible for external call.

Contract constructor

Any contract must have a contract constructor function. This function is called in CreateContract Transaction. The function must be named _constructor.

#[action]
fn _constructor() {
...

This method is used to initialize the contract when it is placed in the network. Most often – to set starting values, roles and so on.

The function must also be marked with the action attribute. The presence or absence of arguments depends on the logic of your constructor.

Available functions

The most relevant list of available functions can be found at https://docs.rs/we-cdk/latest/we_cdk/.

Main components of the we-cdk

crates/cargo-we

The utility is designed to work with smart contracts: project creation and biuld, utilities for WASM and WAT.

crates/cdk

Rust library to write WASM smart contracts.

crates/codegen

WEVM bindings and algorithms for intermediate representation.

crates/proc-macro

Procedure macros to generate code for WASM contracts.

examples

Contract examples.

See also