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 diffent available clients and methods from the Contract Clients
can be found in the .client.ts
files:
Client name | Functionality | Methods list |
---|---|---|
Coconut Bandwidth Client | Manages the depositing and release of funds. Tracks double spending. | Coconut Bandwidth (opens in a new tab) |
Coconut DKG Client | Allows signers partcipating in issuing Coconut credentials to derive keys to be used. | Coconut DKG (opens in a new tab) |
Cw3FlexMultisig Client | Used 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 Client | Used 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 Client | Manages the network topology of the mixnet, tracking delegations and rewarding | Mixnet (opens in a new tab) |
Name Service Client | Operates as a directory of user-defined aliases, analogous to a Domain Name System (DNS). | Name service (opens in a new tab) |
Service provider Directory Client | Allows users to register their service provider in a public directory. | Service Provider (opens in a new tab) |
Vesting Client | Manages NYM token vesting functionality. | Vesting (opens in a new tab) |
Depending on your app or project's architecture, this could be any of the ESM or CJS versions of the Contract Clients
.
Set-up your environment
Create your directory and set-up your app environment:
npx create-react-app my-app
Installation
Install the package and its dependencies from Cosmos Stargate:
npm install @nymproject/contract-clients @cosmjs/cosmwasm-stargate @cosmjs/proto-signing
Query clients
Imports
Import the contracts' client in your app:
import { contracts } from '@nymproject/contract-clients';
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
Polyfills
You will need to install:
npm install --save url fs assert crypto-browserify stream-http https-browserify os-browserify buffer stream-browserify process react-app-rewired
and create a config-overrides.js
file:
const webpack = require('webpack');
module.exports = function override(config, env) {
config.resolve.fallback = {
url: require.resolve('url'),
fs: require.resolve('fs'),
assert: require.resolve('assert'),
crypto: require.resolve('crypto-browserify'),
http: require.resolve('stream-http'),
https: require.resolve('https-browserify'),
os: require.resolve('os-browserify/browser'),
buffer: require.resolve('buffer'),
stream: require.resolve('stream-browserify'),
};
config.plugins.push(
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer'],
}),
);
return config;
}
Update your package.json
file:
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject" // don't change the eject
},
Example: using the Mixnet smart contract client to query
In this example, we will use the MixnetQueryClient
from 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(null);
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 contract address (which is different on mainnet, QA, etc)
);
console.log("client:", client)
const result = await client.getMixNodesDetailed({});
console.log(result)
setMixnodes(result.nodes)
}
useEffect(() => {
fetchMixnodes();
}, [])
return(
<>
<table>
<tbody>
{mixnodes?.map((value, index) => {
return(
<tr key={index}>
<td> {value?.bond_information?.mix_node?.identity_key} </td>
</tr>
)
})
}
</tbody>
</table>
</>
)
}
Execute clients
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 MixnetClient
and the signer
from the Contract Clients
(opens in a new tab) to execute methods.
Note that for the settings.ts
file, we have used 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.ts";
export default function Exec() {
let signer = null;
let address = null;
let signerMixnetClient = null;
let cosmWasmSigningClient = null;
let mixId = null;
let amountToDelegate = null;
let balance = null;
let nodeAddress = null;
let amountToSend = null;
let delegations = null;
async function ExecuteOnNyx() {
// Signer
try {
// Generate a signer from a mnemonic
signer = await DirectSecp256k1HdWallet.fromMnemonic(settings.mnemonic, {
prefix: "n",
});
const accounts = await signer.getAccounts();
address = accounts[0].address;
} catch (error) {
console.error("Problem getting the signer: ", error);
}
try {
const cosmWasmClient = await SigningCosmWasmClient.connectWithSigner(
settings.url,
signer,
{
gasPrice: GasPrice.fromString("0.025unym"),
}
);
cosmWasmSigningClient = cosmWasmClient;
try {
balance = await cosmWasmSigningClient?.getBalance(address, "unym");
console.log("balance", balance);
} catch (error) {
console.error("problem geting the balance: ", error);
}
const mixnetClient = new contracts.Mixnet.MixnetClient(
cosmWasmSigningClient,
settings.address, // sender (that account of the signer)
settings.mixnetContractAddress // contract address (different on mainnet, QA, etc)
);
signerMixnetClient = mixnetClient;
} catch (error) {
console.error("Problem getting the cosmWasmSigningClient: ", error);
}
}
// 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;
}
try {
const res = await signerMixnetClient.delegateToMixnode(
{ mixId },
"auto",
undefined,
[{ amount: `${amountToDelegate}`, denom: "unym" }]
);
console.log("delegations: ", res);
} catch (error) {
console.error(error);
}
};
// Undelegate all
const doUndelegateAll = async () => {
if (!signerMixnetClient) {
return;
}
console.log("delegations", delegations);
try {
for (const delegation of delegations.delegations) {
await signerMixnetClient.undelegateFromMixnode(
{ mixId: delegation.mix_id },
"auto"
);
}
} catch (error) {
console.error(error);
}
};
// Sending tokens
const doSendTokens = async () => {
const memo = "test sending tokens";
try {
const res = await cosmWasmSigningClient.sendTokens(
settings.address,
nodeAddress,
[{ amount: amountToSend, denom: "unym" }],
"auto",
memo
);
console.log("res", res);
} catch (error) {
console.error(error);
}
};
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>
);
}