Developers
Live Playground
3.2. Basic wallet operations

Wallet

Interactive wallet operations on the Nyx Sandbox testnet. This demo walks through connecting to the chain, sending tokens, and querying delegations, all from the browser using @nymproject/contract-clients.

This connects to the Sandbox testnet. No real tokens are involved.

Connect

Create or restore a wallet from a mnemonic to connect to the Nyx Sandbox testnet.

Connect to your testnet account
Your testnet account:

Enter the mnemonic

Please, enter your mnemonic to receive your account information

FormattedWalletConnectCode.tsx
import React from 'react';
import { Coin } from '@cosmjs/stargate';
import Button from '@mui/material/Button';
import Paper from '@mui/material/Paper';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import TextField from '@mui/material/TextField';
 
// Connect method on Parent Component
const getSignerAccount = async () => {
  setAccountLoading(true);
  try {
    const signer = await signerAccount(mnemonic);
    const accounts = await signer.getAccounts();
    if (accounts[0]) {
      setAccount(accounts[0].address);
    }
  } catch (error) {
    console.error(error);
  }
  setAccountLoading(false);
};
 
// Get Balance on Parent Component
const getBalance = useCallback(async () => {
  setBalanceLoading(true);
  try {
    const newBalance = await signerCosmosWasmClient?.getBalance(account, 'unym');
    setBalance(newBalance);
  } catch (error) {
    console.error(error);
  }
  setBalanceLoading(false);
}, [account, signerCosmosWasmClient]);
 
const getClients = async () => {
  setClientLoading(true);
  try {
    setSignerCosmosWasmClient(await fetchSignerCosmosWasmClient(mnemonic));
    setSignerClient(await fetchSignerClient(mnemonic));
  } catch (error) {
    console.error(error);
  }
  setClientLoading(false);
};
 
const connect = () => {
  getSignerAccount();
  getClients();
};
 
// Get Signner Account on Parent Component
const getSignerAccount = async () => {
  setAccountLoading(true);
  try {
    const signer = await signerAccount(mnemonic);
    const accounts = await signer.getAccounts();
    if (accounts[0]) {
      setAccount(accounts[0].address);
    }
  } catch (error) {
    console.error(error);
  }
  setAccountLoading(false);
};
 
export const ConnectWallet = ({
  setMnemonic,
  connect,
  mnemonic,
  accountLoading,
  clientLoading,
  balanceLoading,
  account,
  balance,
  connectButtonText,
}: {
  setMnemonic: (value: string) => void;
  connect: () => void;
  mnemonic: string;
  accountLoading: boolean;
  clientLoading: boolean;
  balanceLoading: boolean;
  account: string;
  balance: Coin;
  connectButtonText: string;
}) => {
  return (
    <Paper style={{ marginTop: '1rem', padding: '1rem' }}>
      <Typography variant="h5" textAlign="center">
        Connect to your account
      </Typography>
      <Box padding={3}>
        <Typography variant="h6">Your account</Typography>
        <Box marginY={3}>
          <Typography variant="body1" marginBottom={3}>
            Enter the mnemonic
          </Typography>
          <TextField
            type="text"
            placeholder="mnemonic"
            onChange={(e) => setMnemonic(e.target.value)}
            fullWidth
            multiline
            maxRows={4}
            sx={{ marginBottom: 3 }}
          />
          <Button
            variant="outlined"
            onClick={() => connect()}
            disabled={!mnemonic || accountLoading || clientLoading || balanceLoading}
          >
            {connectButtonText}
          </Button>
        </Box>
        {account && balance ? (
          <Box>
            <Typography variant="body1">Address: {account}</Typography>
            <Typography variant="body1">
              Balance: {balance?.amount} {balance?.denom}
            </Typography>
          </Box>
        ) : (
          <Box>
            <Typography variant="body1">Please, enter your mnemonic to receive your account information</Typography>
          </Box>
        )}
      </Box>
    </Paper>
  );
};

Send Tokens

Transfer testnet tokens between accounts.

Send Tokens
FormattedWalletSendTokensCode.tsx
import React, { useState } from 'react';
import Button from '@mui/material/Button';
import Paper from '@mui/material/Paper';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import TextField from '@mui/material/TextField';
 
// Send tokens on Parent component
  const doSendTokens = async (amount: string) => {
    const memo = 'test sending tokens';
    setSendingTokensLoader(true);
    try {
      const res = await signerCosmosWasmClient.sendTokens(
        account,
        recipientAddress,
        [{ amount, denom: 'unym' }],
        'auto',
        memo,
      );
      setLog((prev) => [
        ...prev,
        <div key={JSON.stringify(res, null, 2)}>
          <code style={{ marginRight: '2rem' }}>{new Date().toLocaleTimeString()}</code>
          <pre>{JSON.stringify(res, null, 2)}</pre>
        </div>,
      ]);
    } catch (error) {
      console.error(error);
    }
    setSendingTokensLoader(false);
  };
 
export const SendTokes = ({
  setRecipientAddress,
  doSendTokens,
  sendingTokensLoader,
}: {
  setRecipientAddress: (value: string) => void;
  doSendTokens: (amount: string) => void;
  sendingTokensLoader: boolean;
}) => {
  const [tokensToSend, setTokensToSend] = useState<string>();
 
  return (
    <Paper style={{ marginTop: '1rem', padding: '1rem' }}>
      <Box padding={3}>
        <Typography variant="h6">Send Tokens</Typography>
        <Box marginTop={3} display="flex" flexDirection="column">
          <TextField
            type="text"
            placeholder="Recipient Address"
            onChange={(e) => setRecipientAddress(e.target.value)}
            size="small"
          />
          <Box marginY={3} display="flex" justifyContent="space-between">
            <TextField
              type="text"
              placeholder="Amount"
              onChange={(e) => setTokensToSend(e.target.value)}
              size="small"
            />
            <Button variant="outlined" onClick={() => doSendTokens(amount)} disabled={sendingTokensLoader}>
              {sendingTokensLoader ? 'Sending...' : 'SendTokens'}
            </Button>
          </Box>
        </Box>
      </Box>
    </Paper>
  );
};

Delegations

Query staking delegations for an account.

Delegations

Make a delegation

Your delegations:

You do not have delegations

FormattedWalletDelegationsCode.tsx
import React from 'react';
import Button from '@mui/material/Button';
import Paper from '@mui/material/Paper';
import Box from '@mui/material/Box';
import { TableBody, TableCell, TableHead, TableRow, TextField, Typography } from '@mui/material';
import Table from '@mui/material/Table';
 
// Get Delegations on parent component
  const getDelegations = useCallback(async () => {
    const newDelegations = await signerClient.getDelegatorDelegations({
      delegator: settings.address,
    });
    setDelegations(newDelegations);
  }, [signerClient]);
 
// Make a Delegation on parent component
  const doDelegate = async ({ mixId, amount }: { mixId: number; amount: number }) => {
    if (!signerClient) {
      return;
    }
    setDelegationLoader(true);
    try {
      const res = await signerClient.delegateToMixnode({ mixId }, 'auto', undefined, [
        { amount: `${amount}`, denom: 'unym' },
      ]);
      console.log('res', res);
      setLog((prev) => [
        ...prev,
        <div key={JSON.stringify(res, null, 2)}>
          <code style={{ marginRight: '2rem' }}>{new Date().toLocaleTimeString()}</code>
          <pre>{JSON.stringify(res, null, 2)}</pre>
        </div>,
      ]);
    } catch (error) {
      console.error(error);
    }
    setDelegationLoader(false);
  };
 
  // Undelegate All on Parent Component
  const doUndelegateAll = async () => {
    if (!signerClient) {
      return;
    }
    setUndeledationLoader(true);
    try {
      // eslint-disable-next-line no-restricted-syntax
      for (const delegation of delegations.delegations) {
        // eslint-disable-next-line no-await-in-loop
        await signerClient.undelegateFromMixnode({ mixId: delegation.mix_id }, 'auto');
      }
    } catch (error) {
      console.error(error);
    }
    setUndeledationLoader(false);
  };
 
  // Withdraw Rewards on Parent Component
  const doWithdrawRewards = async () => {
    const delegatorAddress = '';
    const validatorAdress = '';
    const memo = 'test sending tokens';
    setWithdrawLoading(true);
    try {
      const res = await signerCosmosWasmClient.withdrawRewards(delegatorAddress, validatorAdress, 'auto', memo);
      setLog((prev) => [
        ...prev,
        <div key={JSON.stringify(res, null, 2)}>
          <code style={{ marginRight: '2rem' }}>{new Date().toLocaleTimeString()}</code>
          <pre>{JSON.stringify(res, null, 2)}</pre>
        </div>,
      ]);
    } catch (error) {
      console.error(error);
    }
    setWithdrawLoading(false);
  };
 
import React, { useState } from 'react';
import Button from '@mui/material/Button';
import Paper from '@mui/material/Paper';
import Box from '@mui/material/Box';
import { TableBody, TableCell, TableHead, TableRow, TextField, Typography } from '@mui/material';
import Table from '@mui/material/Table';
 
export const Delegations = ({
  delegations,
  doDelegate,
  delegationLoader,
  doUndelegateAll,
  undeledationLoader,
  doWithdrawRewards,
  withdrawLoading,
}: {
  delegations: any;
  doDelegate: ({ mixId, amount }: { mixId: number; amount: number }) => void;
  delegationLoader: boolean;
  doUndelegateAll: () => void;
  undeledationLoader: boolean;
  doWithdrawRewards: () => void;
  withdrawLoading: boolean;
}) => {
  const [delegationNodeId, setDelegationNodeId] = useState<string>();
  const [amountToBeDelegated, setAmountToBeDelegated] = useState<string>();
 
  return (
    <Paper style={{ marginTop: '1rem', padding: '1rem' }}>
      <Box padding={3}>
        <Typography variant="h6">Delegations</Typography>
        <Box marginY={3}>
          <Box marginY={3} display="flex" flexDirection="column">
            <Typography marginBottom={3} variant="body1">
              Make a delegation
            </Typography>
            <TextField
              type="text"
              placeholder="Mixnode ID"
              onChange={(e) => setDelegationNodeId(e.target.value)}
              size="small"
            />
            <Box marginTop={3} display="flex" justifyContent="space-between">
              <TextField
                type="text"
                placeholder="Amount"
                onChange={(e) => setAmountToBeDelegated(e.target.value)}
                size="small"
              />
              <Button
                variant="outlined"
                onClick={() =>
                  doDelegate({ mixId: parseInt(delegationNodeId, 10), amount: parseInt(amountToBeDelegated, 10) })
                }
                disabled={delegationLoader}
              >
                {delegationLoader ? 'Delegation in process...' : 'Delegate'}
              </Button>
            </Box>
          </Box>
        </Box>
        <Box marginTop={3}>
          <Typography variant="body1">Your delegations</Typography>
          <Box marginBottom={3} display="flex" flexDirection="column">
            {!delegations?.delegations?.length ? (
              <Typography>You do not have delegations</Typography>
            ) : (
              <Box>
                <Table size="small">
                  <TableHead>
                    <TableRow>
                      <TableCell>MixId</TableCell>
                      <TableCell>Owner</TableCell>
                      <TableCell>Amount</TableCell>
                      <TableCell>Cumulative Reward Ratio</TableCell>
                    </TableRow>
                  </TableHead>
                  <TableBody>
                    {delegations?.delegations.map((delegation: any) => (
                      <TableRow key={delegation.mix_id}>
                        <TableCell>{delegation.mix_id}</TableCell>
                        <TableCell>{delegation.owner}</TableCell>
                        <TableCell>{delegation.amount.amount}</TableCell>
                        <TableCell>{delegation.cumulative_reward_ratio}</TableCell>
                      </TableRow>
                    ))}
                  </TableBody>
                </Table>
              </Box>
            )}
          </Box>
          {delegations && (
            <Box marginBottom={3}>
              <Button variant="outlined" onClick={() => doUndelegateAll()} disabled={undeledationLoader}>
                {undeledationLoader ? 'Undelegating...' : 'Undelegate All'}
              </Button>
            </Box>
          )}
          <Box>
            <Button variant="outlined" onClick={() => doWithdrawRewards()} disabled={withdrawLoading}>
              {withdrawLoading ? 'Doing withdraw...' : 'Withdraw rewards'}
            </Button>
          </Box>
        </Box>
      </Box>
    </Paper>
  );
};