Skip to main content

Tokens and multi-currency support

This tutorial explains how =nil; handles tokens and multi-currency operations.

Definition

=nil; supplies a basic 'default' currency that is distributed by the Faucet contract.

The network also has a multi-currency mechanism. All accounts (smart contracts) can be paid in any number of arbitrary currencies created either by the account owner or other accounts. Currency creation is dedicated to a special precompiled contract (Minter), and anyone can request the creation, minting, and withdrawal of new currencies.

info

While custom currencies can be transferred between accounts, they cannot be used for paying for essential functionalities of =nil; such as deploying contracts or sending async calls.

info

When a currency is created, it is assigned an ID derived from the address of the currency owner. A contract can only be the owner of one currency. While non-owners can transfer different currencies between accounts, they cannot mint any currencies that they do not own.

Usage

Via the CLI

To create a new currency and withdraw it to the address of the owner:

nil minter create-currency OWNER_ADDRESS AMOUNT NAME 
nil minter withdraw-currency OWNER_ADDRESS AMOUNT OWNER_ADDRESS 

To mint an existing currency (which automatically withdraws it to the owner):

nil minter mint-currency OWNER_ADDRESS 50000 

To burn an existing currency (the supply is automatically reduced at the address of the owner):

nil minter burn-currency OWNER_ADDRESS AMOUNT 

Via the client library

tip

To get the ID of a currency, simply call the hexToBigInt() function on the owner's address.

To create a new currency and withdraw it:

const client = new PublicClient({
transport: new HttpTransport({
endpoint: RPC_ENDPOINT,
}),
shardId: 1,
});

const faucet = new Faucet(client);

const signer = new LocalECDSAKeySigner({
privateKey: generateRandomPrivateKey(),
});

const pubkey = await signer.getPublicKey();
const wallet = new WalletV1({
pubkey: pubkey,
salt: BigInt(Math.floor(Math.random() * 10000)),
shardId: 1,
client,
signer,
});

const walletAddress = wallet.getAddressHex();

const faucetHash = await faucet.withdrawToWithRetry(
walletAddress,
convertEthToWei(1)
);

await waitTillCompleted(client, 1, faucetHash);

await wallet.selfDeploy(true);

{
const hashMessage = await wallet.setCurrencyName("MY_TOKEN");
await waitTillCompleted(client, 1, hashMessage);
}

{
const hashMessage = await wallet.mintCurrency(100_000_000n);
await waitTillCompleted(client, 1, hashMessage);
}

To burn an existing currency:

{
const hashMessage = await wallet.burnCurrency(50_000_000n);
await waitTillCompleted(client, 1, hashMessage);
}

Example

This example creates a wallet that stores three currencies: the default token, and two custom currencies.

Via the CLI

Create a new wallet:

nil wallet new 

Create a new currency with the new wallet as its owner and withdraw it:

nil minter create-currency WALLET_ADDRESS 50000 customToken 
nil minter withdraw-currency WALLET_ADDRESS 50000 WALLET_ADDRESS 

Create another currency with another smart contract as its owner:

nil minter create-currency CONTRACT_ADDRESS 30000 newToken 

Withdraw the new currency back to the wallet:

nil minter withdraw-currency CONTRACT_ADDRESS 30000 WALLET_ADDRESS 

Check the wallet currencies:

nil contract currencies WALLET_ADDRESS 

The following response should appear:

Contract currencies:
Balance: 50000 CurrencyId=[CURRENCY_ID_ONE]
Balance: 30000 CurrencyId=[CURRENCY_ID_TWO]

Via the client library

Create two new wallets:

const client = new PublicClient({
transport: new HttpTransport({
endpoint: RPC_ENDPOINT,
}),
shardId: 1,
});

const faucet = new Faucet(client);

const signer = new LocalECDSAKeySigner({
privateKey: generateRandomPrivateKey(),
});

const pubkey = await signer.getPublicKey();
const wallet = new WalletV1({
pubkey: pubkey,
salt: BigInt(Math.floor(Math.random() * 10000)),
shardId: 1,
client,
signer,
});

const walletAddress = wallet.getAddressHex();

const faucetHash = await faucet.withdrawToWithRetry(
walletAddress,
convertEthToWei(1)
);

await waitTillCompleted(client, 1, faucetHash);

await wallet.selfDeploy(true);

const walletTwo = new WalletV1({
pubkey: pubkey,
salt: BigInt(Math.floor(Math.random() * 10000)),
shardId: 1,
client,
signer,
});

const walletTwoAddress = walletTwo.getAddressHex();

const faucetTwoHash = await faucet.withdrawToWithRetry(
walletTwoAddress,
convertEthToWei(1)
);

await waitTillCompleted(client, 1, faucetTwoHash);

await walletTwo.selfDeploy(true);

{
const hashMessage = await wallet.setCurrencyName("MY_TOKEN");
await waitTillCompleted(client, 1, hashMessage);
}

{
const hashMessage = await walletTwo.setCurrencyName("ANOTHER_TOKEN");
await waitTillCompleted(client, 1, hashMessage);
}

Create a new currency for Wallet 1 and withdraw it:

{
const hashMessage = await wallet.mintCurrency(100_000_000n);
await waitTillCompleted(client, 1, hashMessage);
}

{
const hashMessage = await wallet.mintCurrency(50_000_000n);
await waitTillCompleted(client, 1, hashMessage);
}

Create a new currency for Wallet 2 and send it to Wallet 1:

const transferMessage = walletTwo.sendMessage({
to: walletAddress,
value: 1_000_000n,
feeCredit: 5_000_000n,
tokens: [
{
id: hexToBigInt(walletTwoAddress),
amount: 50_000_000n
}
]
});
const tokens = await client.getCurrencies(walletAddress, "latest");