Skip to main content

=nil; 101

This tutorial acts as a 'primer' for working with =nil;. It explains how to perform essential actions with the cluster: creating a new wallet, deploying a contract, and calling a method inside this contract.

tip

This tutorial focuses on the =nil; CLI. To learn more about using Nil.js to create a wallet, deploy smart contracts, and send messages to these contracts, refer to the Cookbook.

Set up the =nil; CLI

The =nil; CLI is an easy-to-use tool for interacting with the cluster.

To install the CLI:

curl -fsSL https://github.com/NilFoundation/nil_cli/raw/master/install.sh | bash

To generate a new private key and set inside the CLI config:

nil keygen new

To point the CLI toward the RPC endpoint:

nil config set rpc_endpoint RPC_ENDPOINT

To set the faucet endpoint necessary for creating a new wallet:

nil config set faucet_endpoint FAUCET_GLOBAL

Create a new wallet

In =nil; a wallet is just a smart contract that can handle payments. There are no other structural differences between a wallet and a smart contract, which means that wallets can support any logic that can be expressed inside a smart contract.

Flow

To create a new wallet:

nil wallet new

Expected response:

Contract WALLET_ADDRESS balance is topped up by 100000000
Contract address: WALLET_ADDRESS

To check the wallet bytecode:

nil contract code WALLET_ADDRESS

To request a 'top up':

nil wallet top-up 1000000

To see the wallet address and its public key:

nil wallet info

Advanced tutorials

Deploy a smart contract

In =nil; contracts can be deployed on different execution shards that act as separate blockchains. Contracts can communicate with contracts on other shards, avoiding state fragmentation.

Any smart contract deployed in Ethereum-compatible networks and written in Solidity can be redeployed to =nil;

Flow

Create a new contract containing this code:

pragma solidity ^0.8.0;

contract Counter {
uint256 private value;

event ValueChanged(uint256 newValue);

receive() external payable {}

function increment() public {
value += 1;
emit ValueChanged(value);
}

function getValue() public view returns (uint256) {
return value;
}

function verifyExternal(
uint256 hash,
bytes memory authData
) external pure returns (bool) {
return true;
}
}

Access contract in the Playground

Compile the contract:

solc -o path/to/Counter --bin --abi path/to/Counter.sol --overwrite 

Deploy a contract with the =nil; CLI:

nil wallet deploy path/to/Counter.bin --salt SALT

Expected output:

Contract address: COUNTER_ADDRESS
Transaction hash: TRANSACTION_HASH (shard 1)

Call the increment() function:

nil wallet send-message COUNTER_ADDRESS increment --abi path/to/Counter.abi 

Retrieve the result:

nil contract call-readonly COUNTER_ADDRESS getValue --abi path/to/Counter.abi 

Advanced tutorials

Make a cross-shard call

When a smart contract makes a call to another smart contract deployed on a separate shard, the destination shard retrieves the resulting message and processes it. To make thing simpler, the Nil.sol library provides the async_call() function: it calls a special precompiled contract that allows for easily passing messages between shards.

Flow

Create a new contract with the following code:

pragma solidity ^0.8.9;

import "@nilfoundation/smart-contracts/contracts/Nil.sol";

contract Caller {
using Nil for address;

receive() external payable {}

function call(address dst) public {
Nil.asyncCall(
dst,
msg.sender,
0,
abi.encodeWithSignature("increment()")
);
}

function verifyExternal(
uint256,
bytes calldata
) external pure returns (bool) {
return true;
}
}

Access contract in the Playground

Compile it and deploy it to Shard 2:

solc -o path/to/Caller --bin --abi path/to/Caller.sol --overwrite 
nil wallet deploy path/to/Caller.bin --shard-id 2 --salt SALT

Expected output:

Contract address: CALLER_ADDRESS
Transaction hash: TRANSACTION_HASH (shard 1)

Note that CALLER_ADDRESS starts with 0x0002, which indicates that Caller is indeed deployed on Shard 2.

Send tokens to Caller and call the call() function:

nil wallet send-tokens CALLER_ADDRESS 3000000 
nil wallet send-message CALLER_ADDRESS call COUNTER_ADDRESS --abi path/to/Caller.abi 

Retrieve the result:

nil contract call-readonly COUNTER_ADDRESS getValue --abi path/to/Counter.abi 

Advanced tutorials

Nil.js

Refer to the Cookbook to learn how this flow would look like when using Nil.js.

Tokens and multi-currency

=nil; has base tokens that are used to pay for essential functionalities such as deploying contracts.

However, each contract can also create a custom currency. A contract can only be the owner of one custom currency. While custom currencies can be transferred between contracts, they cannot be used to pay for operations inside =nil;.

info

Non-owners cannot perform any operations with a custom currency.

Flow

Create a new wallet:

nil wallet new --salt SALT

Create a new currency and withdraw it:

nil minter create-currency NEW_WALLET_ADDRESS 5000 new-currency 

Check the currencies of the wallet:

nil contract currencies NEW_WALLET_ADDRESS 

Expected output:

Contract currencies:
Balance: 50000 CurrencyId=CURRENCY_ID

Advanced tutorials