Developers
Step-by-step examples
3. Nym Smart Contracts

Nym Smart Contract Clients

As previously mentioned, to query or execute on any of the Nym contracts, you'll need to use one of the Contract Clients (opens in a new tab), which contains read-only query and signing clients for all of Nym's smart contracts.

Contract Clients list

Lists of the different available clients and methods from the Contract Clients can be found in the .client.ts files:

Client nameFunctionalityMethods list
Coconut Bandwidth ClientManages the depositing and release of funds. Tracks double spending.Coconut Bandwidth (opens in a new tab)
Coconut DKG ClientAllows signers participating in issuing Coconut credentials to derive keys to be used.Coconut DKG (opens in a new tab)
Cw3FlexMultisig ClientUsed by the Coconut APIs to issue credentials. This (opens in a new tab) is a multisig contract that is backed by the cw4 (group) contract, which independently maintains the voter set.Cw3Flex Multisig (opens in a new tab)
Cw4Group ClientUsed by the Coconut APIs to issue credentials. Cw4 Group (opens in a new tab) stores a set of members along with an admin, and allows the admin to update the state.Cw4Group (opens in a new tab)
Mixnet ClientManages the network topology of the mixnet, tracking delegations and rewards.Mixnet (opens in a new tab)
Name Service ClientOperates as a directory of user-defined aliases, analogous to a Domain Name System (DNS).Name service (opens in a new tab)
Service provider Directory ClientAllows users to register their service provider in a public directory.Service Provider (opens in a new tab)
Vesting ClientManages NYM token vesting functionality.Vesting (opens in a new tab)
Environment Setup

Begin by creating a directory and configuring your application environment:

npm create vite@latest

During the environment setup, choose React and subsequently opt for Typescript if you want your application to function smoothly following this tutorial. Next, navigate to your application directory and run the following commands:

cd < YOUR_APP >
npm i 
npm run dev
Installation

Install the packages and their dependencies if you don't already have them:

npm install @nymproject/contract-clients @cosmjs/cosmwasm-stargate 

Query clients

In the src folder, open the App.tsx file and delete all the code.

Imports

Import the contracts' client in your app:

import { contracts } from '@nymproject/contract-clients';
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
Example: using the mixnet smart contract client to query

In this example, we will use the MixnetQueryClientfrom the Contract Clients to simply query the contract and return a list of mixnodes.

import "./App.css";
import { contracts } from "@nymproject/contract-clients";
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
import { useEffect, useState } from "react";
 
export default function Mixnodes() {
 
  const [mixnodes, setMixnodes] = useState<any>([]);
 
  async function fetchMixnodes(){
    // Set-up the CosmWasm Client
    const cosmWasmClient = await SigningCosmWasmClient.connect("wss://rpc.nymtech.net:443");
    const client = new contracts.Mixnet.MixnetQueryClient(
      cosmWasmClient,
      "n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr" // The mainnet mixnet contract address (which will be different on mainnet, QA, etc)
    );
    const result = await client.getMixNodesDetailed({});
    setMixnodes(result.nodes)
  }
 
  useEffect(() => {
    fetchMixnodes();
  }, [])
 
  return(
    <>
      <table>
        <tbody>
          {mixnodes?.map((value: any, index: number) => {
            return(
              <tr key={index}>
                <td> {value?.bond_information?.mix_node?.identity_key} </td>
              </tr>
            )
          })
          }
        </tbody>
      </table>
    </>
  )
}

By pasting the above code in the App.tsx file and npm run dev your app from the terminal, you should see an unstyled printed list of Nym mixnodes!

Execute clients

Installation

Install the packages and their dependencies if you don't already have them:

npm install @nymproject/contract-clients @cosmjs/cosmwasm-stargate @cosmjs/proto-signing
Imports

Import the contracts' execute clients in your app:

import { contracts } from '@nymproject/contract-clients';
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
Example: using the Mixnet smart contract client to execute methods

In this example, we will use the MixnetClientand the signer from the Contract Clients (opens in a new tab) to execute methods.

Note that you will need to create a settings.ts file (here created in the same directory), using the following structure:

 
export const mySettings = {
    url: "wss://rpc.nymtech.net:443",
    mixnetContractAddress: '<ENTER MIXNET CONTACT ADDRESS HERE>',
    mnemonic: '<ENTER MNEMONIC HERE>',
    address: '<ENTER NYM ADDRESS HERE>'
};
 
export const settings = mySettings;
import "./App.css";
import { contracts } from "@nymproject/contract-clients";
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { GasPrice } from "@cosmjs/stargate";
import { settings } from "./settings";
 
export default function Exec() {
  let signer: DirectSecp256k1HdWallet;
  let signerMixnetClient: any;
  let cosmWasmSigningClient: SigningCosmWasmClient;
  let mixId: number;
  let amountToDelegate: string;
  let nodeAddress: string;
  let amountToSend: string;
  let delegations: any;
 
  async function ExecuteOnNyx() {
    // Cosmos client
    signer = await DirectSecp256k1HdWallet.fromMnemonic(settings.mnemonic, {
      prefix: "n",
    });
    const cosmWasmClient = await SigningCosmWasmClient.connectWithSigner(
      settings.url,
      signer,
      {
        gasPrice: GasPrice.fromString("0.025unym"),
      }
    );
    // Save globally
    cosmWasmSigningClient = cosmWasmClient;
 
    // Nym client
    const mixnetClient = new contracts.Mixnet.MixnetClient(
      cosmWasmSigningClient,
      settings.address, // Sender (that account of the signer)
      settings.mixnetContractAddress // Contract address (different on mainnet, QA, etc)
    );
    // Save globally
    signerMixnetClient = mixnetClient;
 
  }
 
  // Get delegations
  const getDelegations = async () => {
    if (!signerMixnetClient) {
      return;
    }
    const delegationsObject = await signerMixnetClient.getDelegatorDelegations({
      delegator: settings.address,
    });
    delegations = delegationsObject;
  };
 
  // Make delegation
  const doDelegation = async () => {
    if (!signerMixnetClient) {
      return;
    }
    const res = await signerMixnetClient.delegateToMixnode(
      { mixId },
      "auto",
      undefined,
      [{ amount: `${amountToDelegate}`, denom: "unym" }]
    );
    console.log(res);
  };
 
  // Undelegate all
  const doUndelegateAll = async () => {
    for (const delegation of delegations.delegations) {
      await signerMixnetClient.undelegateFromMixnode(
        { mixId: delegation.mix_id },
        "auto"
      );
    }
  };
 
  // Sending tokens
  const doSendTokens = async () => {
    const memo = "test sending tokens";
    const res = await cosmWasmSigningClient.sendTokens(
      settings.address,
      nodeAddress,
      [{ amount: amountToSend, denom: "unym" }],
      "auto",
      memo
    );
    console.log(res);
  };
 
  ExecuteOnNyx();
  setTimeout(() => getDelegations(), 1000);
 
  return (
    <div>
      <p>Exec</p>
      <div>
        <p>Send Tokens</p>
        <input
          type="string"
          placeholder="Node Address"
          onChange={(e) => (nodeAddress = e.target.value)}
        />
        <input
          type="number"
          placeholder="Amount"
          onChange={(e) => (amountToSend = e.target.value)}
        />
        <div>
          <button onClick={() => doSendTokens()}>Send Tokens</button>
        </div>
      </div>
      <div>
        <p>Delegate</p>
        <input
          type="number"
          placeholder="Mixnode Id"
          onChange={(e) => (mixId = +e.target.value)}
        />
        <input
          type="number"
          placeholder="Amount"
          onChange={(e) => (amountToDelegate = e.target.value)}
        />
        <div>
          <button onClick={() => doDelegation()}>Delegate</button>
        </div>
        <div>
          <button onClick={() => doUndelegateAll()}>Undelegate All</button>
        </div>
      </div>
    </div>
  );
}