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

Nym Smart Contract Clients

To query or execute on any of the Nym contracts, use the Contract Clients (opens in a new tab), which contain read-only query and signing clients for all of Nym's smart contracts.

Contract Clients

ClientFunctionalityMethods
Coconut BandwidthManages depositing and release of funds. Tracks double spending.Source (opens in a new tab)
Coconut DKGAllows signers to derive keys for issuing Coconut credentials.Source (opens in a new tab)
Cw3FlexMultisigUsed by the Coconut APIs to issue credentials. cw3-flex-multisig (opens in a new tab) backed by cw4.Source (opens in a new tab)
Cw4GroupUsed by the Coconut APIs. Cw4 Group (opens in a new tab) stores members with an admin.Source (opens in a new tab)
MixnetManages the network topology, tracking delegations and rewards.Source (opens in a new tab)
Name ServiceDirectory of user-defined aliases, analogous to DNS.Source (opens in a new tab)
Service Provider DirectoryPublic directory for registering service providers.Source (opens in a new tab)
VestingManages NYM token vesting functionality.Source (opens in a new tab)

Environment Setup

Create a new project with Vite:

npm create vite@latest

Choose React + TypeScript, then:

cd <YOUR_APP>
npm i
npm run dev

Query Example

Installation

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

Querying the Mixnet Contract

This example uses MixnetQueryClient to fetch a paged list of mixnodes from the contract. Create a settings.ts file for your network configuration:

settings.ts
export const settings = {
  url: "wss://rpc.nymtech.net:443",
  mixnetContractAddress: "n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr",
};
App.tsx
import { useEffect, useState } from "react";
import { contracts } from "@nymproject/contract-clients";
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
import { settings } from "./settings";
import Box from "@mui/material/Box";
import CircularProgress from "@mui/material/CircularProgress";
 
const getClient = async () => {
  const cosmWasmClient = await SigningCosmWasmClient.connect(settings.url);
  const client = new contracts.Mixnet.MixnetQueryClient(
    cosmWasmClient,
    settings.mixnetContractAddress
  );
  return client;
};
 
export const Mixnodes = () => {
  const [mixnodes, setMixnodes] = useState<any>();
 
  const getMixnodes = async () => {
    const client = await getClient();
    const { nodes } = await client.getMixNodesDetailed({});
    setMixnodes(nodes);
  };
 
  useEffect(() => {
    getMixnodes();
  }, []);
 
  if (!mixnodes) {
    return (
      <Box sx={{ display: "flex" }}>
        <CircularProgress />
      </Box>
    );
  }
 
  return (
    <div style={{ marginTop: "1rem" }}>
      {mixnodes?.length &&
        mixnodes.map((mixnode: any) => (
          <Box className="codeBox" key={mixnode.bond_information.mix_id}>
            <span style={{ marginRight: "1rem" }}>
              {`id: ${mixnode.bond_information.mix_id}`}
            </span>
            <span>{`owner: ${mixnode.bond_information.owner}`}</span>
          </Box>
        ))}
    </div>
  );
};

Execute Example

Installation

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

Executing Contract Methods

This example uses MixnetClient with a signer to execute methods like delegation.

Update your settings.ts to include signing credentials:

settings.ts
export const settings = {
  url: "wss://rpc.nymtech.net:443",
  mixnetContractAddress: "<ENTER MIXNET CONTRACT ADDRESS>",
  mnemonic: "<ENTER MNEMONIC>",
  address: "<ENTER NYM ADDRESS>",
};
App.tsx
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() {
    signer = await DirectSecp256k1HdWallet.fromMnemonic(settings.mnemonic, {
      prefix: "n",
    });
    const cosmWasmClient = await SigningCosmWasmClient.connectWithSigner(
      settings.url,
      signer,
      { gasPrice: GasPrice.fromString("0.025unym") }
    );
    cosmWasmSigningClient = cosmWasmClient;
 
    const mixnetClient = new contracts.Mixnet.MixnetClient(
      cosmWasmSigningClient,
      settings.address,
      settings.mixnetContractAddress
    );
    signerMixnetClient = mixnetClient;
  }
 
  const getDelegations = async () => {
    if (!signerMixnetClient) return;
    delegations = await signerMixnetClient.getDelegatorDelegations({
      delegator: settings.address,
    });
  };
 
  const doDelegation = async () => {
    if (!signerMixnetClient) return;
    const res = await signerMixnetClient.delegateToMixnode(
      { mixId },
      "auto",
      undefined,
      [{ amount: `${amountToDelegate}`, denom: "unym" }]
    );
    console.log(res);
  };
 
  const doUndelegateAll = async () => {
    for (const delegation of delegations.delegations) {
      await signerMixnetClient.undelegateFromMixnode(
        { mixId: delegation.mix_id },
        "auto"
      );
    }
  };
 
  const doSendTokens = async () => {
    const res = await cosmWasmSigningClient.sendTokens(
      settings.address,
      nodeAddress,
      [{ amount: amountToSend, denom: "unym" }],
      "auto",
      "test sending tokens"
    );
    console.log(res);
  };
 
  ExecuteOnNyx();
  setTimeout(() => getDelegations(), 1000);
 
  return (
    <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)} />
      <button onClick={() => doSendTokens()}>Send Tokens</button>
 
      <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)} />
      <button onClick={() => doDelegation()}>Delegate</button>
      <button onClick={() => doUndelegateAll()}>Undelegate All</button>
    </div>
  );
}