=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.
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;
}
}
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;
}
}
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
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;.
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