Live Playground
3.2. Basic wallet operations

Wallet

Here's a small wallet example using testnet for you to test out!

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
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

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>
  );
};