Introduction
This tutorial acts as a beginer’s guide for NEO C# develpers. Advanced learners may refer to NEO Documentation for more details.
What you will learn
In the sections that follow, we will work you through an example of how to release an NEP-5 asset on NEO blockchain, which includes the tasks of development environment set-up and configuration, smart contract compilation, smart contract deployment, and smart contract invocation on private chain.
- Set up a local network (URL)
- Develop and deploy NEP-5 contracts (URL)
Before you begin
This tutorial is based on the usage of the two full-node NEO clients: NEO-GUI and NEO-CLI. NEO-CLI will be used to set up a private chain accessible by nodes and NEO-GUI will be used to release smart contracts. Detailed information about the clients can be found in NEO Node Introduction.
System environment
NEO-GUI runs in the following environments:
Windows 7 SP1 / Windows 8 / Windows 10
.NET Framework 4.7.1 must be installed for system versions prior to Windows 10.
NEO-CLI runs in the following environments:
- Linux (ubuntu 16.04 and above)
- Windows 10
[!NOTE]
Windows 10 is a recommended choice since NEO-GUI and NEO-CLI will be running at the same time.
This tutorial only describes the occurences on Windows 10. Readers using other systems may refer to relevant chapters in NEO Documentation since environment and dependencies may differ in different systems.
Download clients
-
NEO-GUI
Download the latest Release version at GitHub and run neo-gui.exe.
-
NEO-CLI
Take Windows 10 for example:
Users are not required to install a client. You may get the latest Release version from GitHub by downloading the source code from GitHub or using the following command:
$ git clone https://github.com/neo-project/neo-cli.git
Create wallet files
Users need to create 4 wallet files reserved for private chain set-up, which will be elaborated later. The wallets, which can either be created in NEO-GUI or NEO-CLI, are used to store both NEO account info and the asset info in the accounts. In this section, we will create 4 wallets in NEO-GUI for example and name them 1, 2, 3, 4.json respectively.
- Open NEO-GUI and click
wallet
->create a wallet database
, then follow the instructions shown on the screen. - When the wallet is successfully created, right click the address in the standard account and select
view private key
tod view the account info (address, public key, private key). - Copy the public key of the address for later use.
After you have created 4 wallets and saved the public key, close NEO-GUI and proceed to the next step - Setting up local network.
Setting up local network
We will complete the following tasks in this section:
- Set-up a private chain
- Connect the nodes to the private network
- Retrieve NEO and GAS from genesis block
- Create a wallet file
Setting up a private chain
NEO Official provides a test net for development, debugging and testing purposes. Besides, users may also choose to set up their own private chain where they can operate more flexibly with plenty of test tokens. This article only describes a quick method to set up private chain. For the standard method refer to Build a Private Chain.
Install nodes
At least 4 nodes must reach consensus before NEO private chain is successfully deployed. As such, here we make 4 copies of the neo-cli file folder, which was installed in the early steps, and name them node1, node2, node3, and node4 respectively.
Install plugins
Install the SimplePolicy plugin to activate consensus policy, a mechanism that nodes rely on to reach consensus.
- Download and unzip SimplePolicy.
- Make 4 copies of Plugins File Folder and place each in each of the 4 node files.
Place wallet files
Place each of the 4 wallet files created in NEO-GUI in each of the 4 node files.
Modify config.json
Make following changes to the config.json of each node:
- Change the settings of each port to no-repeat and not occupied by other programs.
- Set the Path and Password under UnlockWallet and define StartConsensus and IsActive as true.
The following configurations may serve as references:
node1/config.json
{
"ApplicationConfiguration": {
"Paths": {
"Chain": "Chain_{0}",
"Index": "Index_{0}"
},
"P2P": {
"Port": 10001,
"WsPort": 10002
},
"RPC": {
"BindAddress": "127.0.0.1",
"Port": 10003,
"SslCert": "",
"SslCertPassword": ""
},
"UnlockWallet": {
"Path": "1.json",
"Password": "11111111",
"StartConsensus": true,
"IsActive": true
}
}
}
node2/config.json
{
"ApplicationConfiguration": {
"Paths": {
"Chain": "Chain_{0}",
"Index": "Index_{0}"
},
"P2P": {
"Port": 20001,
"WsPort": 20002
},
"RPC": {
"BindAddress": "127.0.0.1",
"Port": 20003,
"SslCert": "",
"SslCertPassword": ""
},
"UnlockWallet": {
"Path": "2.json",
"Password": "11111111",
"StartConsensus": true,
"IsActive": true
}
}
}
node3/config.json
{
"ApplicationConfiguration": {
"Paths": {
"Chain": "Chain_{0}",
"Index": "Index_{0}"
},
"P2P": {
"Port": 30001,
"WsPort": 30002
},
"RPC": {
"BindAddress": "127.0.0.1",
"Port": 30003,
"SslCert": "",
"SslCertPassword": ""
},
"UnlockWallet": {
"Path": "3.json",
"Password": "11111111",
"StartConsensus": true,
"IsActive": true
}
}
}
node4/config.json
{
"ApplicationConfiguration": {
"Paths": {
"Chain": "Chain_{0}",
"Index": "Index_{0}"
},
"P2P": {
"Port": 40001,
"WsPort": 40002
},
"RPC": {
"BindAddress": "127.0.0.1",
"Port": 40003,
"SslCert": "",
"SslCertPassword": ""
},
"UnlockWallet": {
"Path": "4.json",
"Password": "11111111",
"StartConsensus": true,
"IsActive": true
}
}
}
Modify protocal.json
Redefine the following parameters of the protocal.json file of each node and keep the consistency of each node settings.
- Magic: The private chain ID can be defined as any integar ranging from 0 to 4294967295.
- StandbyValidators: The public key of the alternate consensus node. Enter the public keys of four wallets.
- SeedList: Define the IP address of the seed node as localhost and the port as the 4 P2P Port configured in config.json.
Below is a recommended configuration for your reference:
{
"ProtocolConfiguration": {
"Magic": 123456,
"AddressVersion": 23,
"SecondsPerBlock": 15,
"StandbyValidators": [
"037ebe29fff57d8c177870e9d9eecb046b27fc290ccbac88a0e3da8bac5daa630d",
"03b34a4be80db4a38f62bb41d63f9b1cb664e5e0416c1ac39db605a8e30ef270cc",
"03cc384ca982168bf6f08922d27c8acc4357d52a7e8ad8281d4af6683e6f63e94d",
"03da4ed85a991134bf45592a5b04d6d71399f23a85843f43e6ac1a5d30f5473711"
],
"SeedList": [
"localhost:10001",
"localhost:20001",
"localhost:30001",
"localhost:40001"
],
"SystemFee": {
"EnrollmentTransaction": 10,
"IssueTransaction": 5,
"PublishTransaction": 5,
"RegisterTransaction": 100
}
}
}
[!Note]
The public keys filled in the “StandbyValidators” section must be replaced with your own public keys of the wallets created before.
Create a shortcut
To start the private chain quickly, here we create a .txt file and input dotnet neo-cli.dll /rpc
. Then rename it to 1Run.cmd and copy it to the directory of the 4 nodes.
Thus far, we have successfully built a private chain. The amended file structure is presented below.
├─node1
│ 1.json
│ 1Run.cmd
│ config.json
│ protocol.json
│
├─node2
│ 1Run.cmd
│ 2.json
│ config.json
│ protocol.json
│
├─node3
│ 1Run.cmd
│ 3.json
│ config.json
│ protocol.json
│
└─node4
1Run.cmd
4.json
config.json
protocol.json
Start private chain
Enter the directory of each node and double click 1Run.cmd
, as shown in the screenshot below:
Right click the command prompt
and click close all windows
to disable the private chain.
Connecting to private chain
After the private chain is successfully built, do the following to connect your nodes to the private chain:
-
Copy the protocal.json file from the directory of whichever node you choose from node1 ~ node4 to overwrite the protocal.json file in NEO-GUI directory.
-
Replace all the “localhost” in the “SeedList” field of the protocal.json with the IPv4 address of the local host.
-
Examine to ensure that the ports defined in config.json do not conflict with the ports of the four consensus nodes.
Otherwise, NEO-GUI and NEO-CLI won’t be able to run at the same time.
Run neo-gui.exe and open the 1.json wallet file. You will see that the connection counts shown on the left corner of the interface is more than 0 and the three values start to increase, which represents wallet height/block height.block head height each.
As of now, we have successfully built a private chain and connected the nodes to the chain.
[!Note]
If the connection counts of NEO-GUI is 0, you may delete the “Chain_xx” and “Index_xx” files in the root directory and reconnect.
Retrieving NEO and GAS
100mn NEO are stored in NEO genesis block and Gas is generated with every new block generated after the private chain is set up. Our next step is to retrieve these NEO and GAS from multi-signature smart contract for test use.
Create multi-signature address
Open the four wallets in sequence in NEO-GUI and follow the instructions below:
-
Right click the margins of the account page and select
create a contract address
->multi-signature
to add multi-signature address to each wallet. -
Input in sequence the saved public keys of the 4 wallets, define the minimum signature required as 3 (consensus node counts/2+1) and click
Confirm
. -
Click
wallet
->reconstruct wallet index
.
[!Note]
You must add multi-signature addresses to all four wallets, otherwise the signature may be deemed invalid.
100mn NEO will show up in the contract address as indicated in the screenshot below.
Transfer NEO to standard address
Take the following steps to transfer NEO from a contract address to a standard address:
-
Randomly open a wallet out of the four wallets and click
transaction
->asset transfer
. -
Input the standard recipient address and transfer the 100mn NEO to this address.
-
The system warns “Transactions are constructed, but with insufficient signatures.” Copy the code line.
-
Open the second wallet and click
transaction
->signature
. -
Paste the code line copied in step 3, click
signature
and copy the code line generated. -
Open the third wallet and click
transaction
->signature
, paste the code line copied in step 5 and clicksignature
.The window will display a
broadcast
button, which indicates that the transaction is signed by the minimum required number of parties and is ready for broadcasting. -
Click
broadcast
to conplete asset transfer.Wait a moment and 100mn NEO will show up in the standard address.
Transfer GAS to standard address
Open the recipient wallet and click advance
-> Claim NeoGas
-> Claim all
.
The following operation is quite similar to NEO transfer operation. Copy the code line indicating insufficient signatures, open the second and third wallet in sequence, sign the transactions and broadcast. If the transaction is successful, the amount will show up in the account as indicated in the screenshot below.
Preparing a wallet file
Now we will create a new wallet file named 0.json and place it in the root directory of the nodes for smart contract release.
- Create a new wallet file named 0.json and copy the default address in case of later use.
- Open the recipient wallets of NEO and GAS, transfer all the wallet assets to 0.json and wait for the transaction to be confirmed.
- Open 0.json and assets is displayed in the account.
[!Note]
Smart contract deployment and invocation cost GAS. Since GAS is generated with every new block generated, causing limited GAS generated on freshly-built private chains, therefore users are advised not to shut down private chains now in order to generate enough GAS in case of later use.
Developing and deploying NEP-5 contract
So far, we have learned how to build a private chain and connect nodes to the chain. The following part will proceed to environment configuration, NEO smart contract coding and compilation and NEO smart contract deployment and invocation on private chain using C# and Windows 10.
We will complete the following tasks in this section:
- Install contract development environment
- Create a NEP-5 contract project
- Compile a contract
- Deploy a contracts
- Invoke a contract
- View contract assets
Installing development environment
Install Visual Studio 2017
Download and install Visual Studio 2017 . Select .NET Core cross-platform development
option during installation.
Install NeoContractPlugin
Open Visual Studio 2017 and click tool
-> extensions and Updates
,click online
on the left column, search NEO and install NeoContractPlugin (the process must be completed online).
Configure neo-compiler
-
Download neo-compiler project to your localhost.
-
Click
file
->open
->project/solutions
in Visual Studio 2017 and select neo-compiler.sln in the project file. -
Right click neon project in the list and click
release
. -
After the release path is configured, click
release
.Upon successful release, a neon.exe file is generated in bin\Release\PublishOutput.
[!Note]
In case of error warning in the process of release: unable to copy file “obj\Release\netcoreapp1.0\win10-x64\neon.dll” as the file cannot be located, which may be be traced to a bug in VS 2017 (e.g. v15.4, 15.5), what you need to do is manually copy
\obj\Release\netcoreapp1.0\neon.dll
to\obj\Release\netcoreapp1.0\win10-x64\
folder and release it again.
Change environment parameter settings
Next we need to add path using the following method to allow neon.exe to be accessible from any point:
-
For Windows10, press Windows+S, input environment parameter and select
edit the account's environment parameters
. -
Select Path and click
edit
: -
Click
create
in the popped up window and input your file filer directory that contains neon.exe, then pressconfirm
.
[!Note]
Do not add a path that contains“…… neon.exe” in the environment parameter field. Remember to input the path of the file folder directory that contains neon.exe instead of the path of neon.exe file.
After the path is added, run CMD or PowerShell for testing purpose (if CMD starts working before the path is added, remember to restart it after adding the path). If no error is reported after inputting neon and the system sends the prompt message containing version number as follows, it means that the environment parameter configuration is successful.
Creating a NEO contract project
Upon completion of the previous steps, you may start to create NEO smart contract project in Visual Studio 2017 (no specific requirement for .NET Framework version):
- Click
file
->create
->project
. - Select
NeoContract
in the list and change settings where necessary, then clickconfirm
.
A C# file will be auto-generated after the project is created with a default class inherited from the SmartContract. As indicated in the screenshot below, now you have a Hello World contract.
Nevertheless, the above only demonstrates a simple data storage method - to store data in private storage area using key-value method.
Editing NEP-5 code
Many developers are curious about how to release their own contract assets on NEO public chain. Now let’s walk through the process on private chain.
-
Download the NEP-5 template from Github.
-
Create a NEO smart contract project in Visual Studio 2017 and name it NEP5.
-
Open NEP5.cs
The code contains basic information of the assets and the methods available to be invoked. You can make changes when needed.
[!NOTE]
If there are red underlines under the code warning that the system is unable to find NEO name space and there is “!” in project references, you may take the following steps:
Right click the solution file in VS, click
manage NuGet package
and update the Neo.SmartContract.Framework to the latest official version in a new page. If the red underlines still exist when program update is completed, you may try double clicking the “!”. If the problem remains unsolved, you may resort to the solutions below::- Download nuget.exe here and copy it to the root directory of NeoContract project.
- Open Power Shell or command prompt (CMD).
- Redirect to the root directory of NeoContract project and run
nuget restore
.
-
Here we make certain modifications to the example files as follows:
- Define total assets and
deploy
method. - Replace “Owner” with the address in 0.json (otherwise the wallet assets won’t be accessible).
The code is as follows:
- Define total assets and
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services.Neo;
using Neo.SmartContract.Framework.Services.System;
using System;
using System.ComponentModel;
using System.Numerics;
namespace NEP5
{
public class NEP5 : SmartContract
{
[DisplayName("transfer")]
public static event Action<byte[], byte[], BigInteger> Transferred;
private static readonly byte[] Owner = "Ad1HKAATNmFT5buNgSxspbW68f4XVSssSw".ToScriptHash(); //Owner Address
private static readonly BigInteger TotalSupplyValue = 10000000000000000;
public static object Main(string method, object[] args)
{
if (Runtime.Trigger == TriggerType.Verification)
{
return Runtime.CheckWitness(Owner);
}
else if (Runtime.Trigger == TriggerType.Application)
{
var callscript = ExecutionEngine.CallingScriptHash;
if (method == "balanceOf") return BalanceOf((byte[])args[0]);
if (method == "decimals") return Decimals();
if (method == "deploy") return Deploy();
if (method == "name") return Name();
if (method == "symbol") return Symbol();
if (method == "supportedStandards") return SupportedStandards();
if (method == "totalSupply") return TotalSupply();
if (method == "transfer") return Transfer((byte[])args[0], (byte[])args[1], (BigInteger)args[2], callscript);
}
return false;
}
[DisplayName("balanceOf")]
public static BigInteger BalanceOf(byte[] account)
{
if (account.Length != 20)
throw new InvalidOperationException("The parameter account SHOULD be 20-byte addresses.");
StorageMap asset = Storage.CurrentContext.CreateMap(nameof(asset));
return asset.Get(account).AsBigInteger();
}
[DisplayName("decimals")]
public static byte Decimals() => 8;
private static bool IsPayable(byte[] to)
{
var c = Blockchain.GetContract(to);
return c == null || c.IsPayable;
}
[DisplayName("deploy")]
public static bool Deploy()
{
if (TotalSupply() != 0) return false;
StorageMap contract = Storage.CurrentContext.CreateMap(nameof(contract));
contract.Put("totalSupply", TotalSupplyValue);
StorageMap asset = Storage.CurrentContext.CreateMap(nameof(asset));
asset.Put(Owner, TotalSupplyValue);
Transferred(null, Owner, TotalSupplyValue);
return true;
}
[DisplayName("name")]
public static string Name() => "GinoMo"; //name of the token
[DisplayName("symbol")]
public static string Symbol() => "GM"; //symbol of the token
[DisplayName("supportedStandards")]
public static string[] SupportedStandards() => new string[] { "NEP-5", "NEP-7", "NEP-10" };
[DisplayName("totalSupply")]
public static BigInteger TotalSupply()
{
StorageMap contract = Storage.CurrentContext.CreateMap(nameof(contract));
return contract.Get("totalSupply").AsBigInteger();
}
#if DEBUG
[DisplayName("transfer")] //Only for ABI file
public static bool Transfer(byte[] from, byte[] to, BigInteger amount) => true;
#endif
//Methods of actual execution
private static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] callscript)
{
//Check parameters
if (from.Length != 20 || to.Length != 20)
throw new InvalidOperationException("The parameters from and to SHOULD be 20-byte addresses.");
if (amount <= 0)
throw new InvalidOperationException("The parameter amount MUST be greater than 0.");
if (!IsPayable(to))
return false;
if (!Runtime.CheckWitness(from) && from.AsBigInteger() != callscript.AsBigInteger())
return false;
StorageMap asset = Storage.CurrentContext.CreateMap(nameof(asset));
var fromAmount = asset.Get(from).AsBigInteger();
if (fromAmount < amount)
return false;
if (from == to)
return true;
//Reduce payer balances
if (fromAmount == amount)
asset.Delete(from);
else
asset.Put(from, fromAmount - amount);
//Increase the payee balance
var toAmount = asset.Get(to).AsBigInteger();
asset.Put(to, toAmount + amount);
Transferred(from, to, amount);
return true;
}
}
}
When the editing is done, the coding part of the smart contract is done.
Compiling contract file
Click generate
->generate solutions
(hotkeys: Ctrl + Shift + B) in the menu to start compilation.
When the compilation is done, NEO smart contract file namedNEP5.avm
is generated in the bin/Debug
directory of the project.
NEP5.abi.json
is a descriptive file of the smart contract, which contains desciptions of the ScriptHash, entry, parameters and return values of the contract. More information about the smart contract ABI can be found in NeoContract ABI.
[!Note]
Given that neon compiles .dll with nep-8 by default, which conflicts with nep-5, thus we need to execute .avm using nep-5 compatible method.
Open Power Shell or command prompt (CMD), enter bin/Debug directory and input the following command (replace nep5.dll with your own project file):
neon nep5.dll --compatible
The new
nep5.avm
file andnep5.abi.json
file will overwrite the old files.
Deploying contract
We may use NEO-GUI to deploy the newly generated contract file.
-
Open 0.json wallet file, click
advance
->deploy contracts
. -
Click
load
to select the compiled contract file in the contract deployment dialog.Copy the contract script hash displayed under the code box for late use in contract invocation.
-
Fill in the params in the information and meta data fields. Do not leave any parameter undefined, otherwise the
deploy
button won’t function properly.For NEP-5 asset contract, the argument is written as 0710 and the return value is 05.
Detailed rules can be referred to Smart Contract Parameters and Return Values。
Check the box of
required to create a storage area
as according to NEP-5 standard, storage areas are used to maintain accounts.No need to check the options
require dynamic invocation
andPayable
. -
After all the params are defined, click
deploy
. -
Click
trial run
in the popped up contract invocation interface. Double check and clickinvoke
.Contract deployment costs about 100-1000 GAS, which is further explained in system fees.
Upon successful deployment, your smart contract is now released to the blockchain.
Invoking contract
Now you may invoke the smart contract released just recently.
-
Click
advance
->contract call
->function call
。 -
Paste the contract scripthash copied in the early step to
ScriptHash
and press search button. Relevant contract information will be displayed automatically. -
Click
...
besidearguments
to enter the edit interface. -
Concerning the smart contract you wrote, [0] represents the function name while [1] the input param of the function (ignore if not exist). If you need to invoke deploy function and release the assets onto the chain, take the following steps: click [0], fill in “deploy” (all in lowercase letters) in the new value, click
update
and close the window. -
Click
trial run
to test the contract. If no error is spotted, clickinvoke
, which may cost several GAS.
Viewing contract assets
Click advance
-> options
in NEO-GUI and fill in the scripthash of the recently deployed assets. The NEP-5 assets will show up in your asset page.
You have successfully released a smart contract on NEO private chain. Congratulations!