started new docs draft
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
```ts copy filename="CosmosKitExample.tsx"
|
||||
import React, { FC } from 'react';
|
||||
import { ChainProvider, useChain } from '@cosmos-kit/react';
|
||||
import { assets, chains } from 'chain-registry';
|
||||
import { wallets as keplr } from '@cosmos-kit/keplr';
|
||||
import { wallets as ledger } from '@cosmos-kit/ledger';
|
||||
import Button from '@mui/material/Button';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Box from '@mui/material/Box';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { Alert, AlertTitle } from '@mui/material';
|
||||
import { Wallet } from '@cosmos-kit/core';
|
||||
import { CosmosKitLedger } from './ledger';
|
||||
import { CosmosKitSign } from './sign';
|
||||
|
||||
const CosmosKitSetup: FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const assetsFixedUp = React.useMemo(() => {
|
||||
const nyx = assets.find((a) => a.chain_name === 'nyx');
|
||||
if (nyx) {
|
||||
const nyxCoin = nyx.assets.find((a) => a.name === 'nyx');
|
||||
if (nyxCoin) {
|
||||
nyxCoin.coingecko_id = 'nyx';
|
||||
}
|
||||
nyx.assets = nyx.assets.reverse();
|
||||
}
|
||||
return assets;
|
||||
}, [assets]);
|
||||
|
||||
const chainsFixedUp = React.useMemo(() => {
|
||||
const nyx = chains.find((c) => c.chain_id === 'nyx');
|
||||
if (nyx) {
|
||||
if (!nyx.staking) {
|
||||
nyx.staking = {
|
||||
staking_tokens: [{ denom: 'unyx' }],
|
||||
lock_duration: {
|
||||
blocks: 10000,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return chains;
|
||||
}, [chains]);
|
||||
|
||||
return (
|
||||
<ChainProvider
|
||||
chains={chainsFixedUp}
|
||||
assetLists={assetsFixedUp}
|
||||
wallets={[...ledger, ...keplr]}
|
||||
signerOptions={{
|
||||
preferredSignType: () => 'amino',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ChainProvider>
|
||||
);
|
||||
};
|
||||
|
||||
function walletRejectMessageOrError(wallet?: Wallet, error?: React.ReactNode) {
|
||||
if (!wallet) {
|
||||
if (!error) {
|
||||
return undefined;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
if (typeof wallet.rejectMessage === 'string') {
|
||||
return wallet.rejectMessage;
|
||||
}
|
||||
return wallet.rejectMessage.source;
|
||||
}
|
||||
const Wrapper: FC<{
|
||||
children?: React.ReactNode;
|
||||
wallet?: Wallet;
|
||||
header?: React.ReactNode;
|
||||
error?: React.ReactNode;
|
||||
disconnect: () => Promise<void>;
|
||||
}> = ({ wallet, disconnect, error, header, children }) => {
|
||||
if (error) {
|
||||
return (
|
||||
<Box mt={4} mb={2}>
|
||||
<Alert severity="error">
|
||||
{wallet && <AlertTitle>Failed to connect to {wallet.prettyName}</AlertTitle>}
|
||||
{wallet && walletRejectMessageOrError(wallet)}
|
||||
</Alert>
|
||||
<Box mt={2}>
|
||||
<Button variant="outlined" onClick={disconnect}>
|
||||
Retry
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Box mt={4} mb={2}>
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
{header && header}
|
||||
<Button variant="outlined" onClick={disconnect}>
|
||||
Disconnect
|
||||
</Button>
|
||||
</Box>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const CosmosKitInner = () => {
|
||||
const {
|
||||
wallet,
|
||||
address,
|
||||
connect,
|
||||
disconnect,
|
||||
isWalletConnecting,
|
||||
isWalletDisconnected,
|
||||
isWalletError,
|
||||
isWalletNotExist,
|
||||
isWalletRejected,
|
||||
} = useChain('nyx');
|
||||
|
||||
if (isWalletError) {
|
||||
return <Wrapper wallet={wallet} error="Oh no! Something went wrong." disconnect={disconnect} />;
|
||||
}
|
||||
|
||||
if (isWalletNotExist) {
|
||||
return <Wrapper wallet={wallet} error="Oh no! Could not connect to that wallet." disconnect={disconnect} />;
|
||||
}
|
||||
|
||||
if (isWalletRejected) {
|
||||
return (
|
||||
<Wrapper
|
||||
wallet={wallet}
|
||||
error="Oh no! Did you authorize the connection to your wallet?"
|
||||
disconnect={disconnect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isWalletDisconnected) {
|
||||
return (
|
||||
<Button variant="outlined" onClick={connect} sx={{ mt: 4 }}>
|
||||
Connect your wallet
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
if (isWalletConnecting) {
|
||||
return <CircularProgress />;
|
||||
}
|
||||
|
||||
// Ledger Hardware Wallet
|
||||
if (wallet.mode === 'ledger') {
|
||||
return (
|
||||
<Wrapper
|
||||
header={
|
||||
<Box>
|
||||
<strong>Connected to {wallet.prettyName}</strong>
|
||||
<Typography>
|
||||
Address: <code>{address}</code>{' '}
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
disconnect={disconnect}
|
||||
>
|
||||
<CosmosKitLedger />
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
// Extension or Wallet Connect
|
||||
return (
|
||||
<Wrapper
|
||||
header={
|
||||
<Box>
|
||||
<strong>Connected to {wallet.prettyName}</strong>
|
||||
<Typography>
|
||||
Address: <code>{address}</code>{' '}
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
disconnect={disconnect}
|
||||
>
|
||||
<CosmosKitSign />
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export const CosmosKit = () => (
|
||||
<CosmosKitSetup>
|
||||
<CosmosKitInner />
|
||||
</CosmosKitSetup>
|
||||
);
|
||||
```
|
||||
@@ -0,0 +1,84 @@
|
||||
```ts copy filename="mixFetchExample.tsx"
|
||||
import React, { useState } from 'react';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Button from '@mui/material/Button';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
import { mixFetch } from '@nymproject/mix-fetch-full-fat';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import type { SetupMixFetchOps } from '@nymproject/mix-fetch';
|
||||
|
||||
const defaultUrl = 'https://nymtech.net/favicon.svg';
|
||||
const args = { mode: 'unsafe-ignore-cors' };
|
||||
|
||||
const mixFetchOptions: SetupMixFetchOps = {
|
||||
preferredGateway: '6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B', // with WSS
|
||||
preferredNetworkRequester:
|
||||
'8rRGWy54oC8drFL9DepMegBt2DLrsqQwCoHMXt9nsnTo.2XjCPVbb4FpQ9hNRcXwb9mTzEAVVk1zf1tcch3wdtNEA@6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B',
|
||||
mixFetchOverride: {
|
||||
requestTimeoutMs: 60_000,
|
||||
},
|
||||
forceTls: true, // force WSS
|
||||
extra: {},
|
||||
};
|
||||
|
||||
export const MixFetch = () => {
|
||||
const [url, setUrl] = useState<string>(defaultUrl);
|
||||
const [html, setHtml] = useState<string>();
|
||||
const [busy, setBusy] = useState<boolean>(false);
|
||||
|
||||
const handleFetch = async () => {
|
||||
try {
|
||||
setBusy(true);
|
||||
setHtml(undefined);
|
||||
const response = await mixFetch(url, args, mixFetchOptions);
|
||||
console.log(response);
|
||||
const resHtml = await response.text();
|
||||
setHtml(resHtml);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: '1rem' }}>
|
||||
<Stack direction="row">
|
||||
<TextField
|
||||
disabled={busy}
|
||||
fullWidth
|
||||
label="URL"
|
||||
type="text"
|
||||
variant="outlined"
|
||||
defaultValue={defaultUrl}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
/>
|
||||
<Button variant="outlined" disabled={busy} sx={{ marginLeft: '1rem' }} onClick={handleFetch}>
|
||||
Fetch
|
||||
</Button>
|
||||
</Stack>
|
||||
|
||||
{busy && (
|
||||
<Box mt={4}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
)}
|
||||
{html && (
|
||||
<>
|
||||
<Box mt={4}>
|
||||
<strong>Response</strong>
|
||||
</Box>
|
||||
<Paper sx={{ p: 2, mt: 1 }} elevation={4}>
|
||||
<Typography fontFamily="monospace" fontSize="small">
|
||||
{html}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,54 @@
|
||||
```ts copy filename="MixnodeContractQueryExample.ts"
|
||||
import { useEffect, useState } from "react";
|
||||
import { contracts } from "@nymproject/contract-clients";
|
||||
import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
|
||||
import { settings } from "./client";
|
||||
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>
|
||||
);
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,103 @@
|
||||
```ts copy filename="MixnetWASMClientExample.tsx"
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { createNymMixnetClient, NymMixnetClient, Payload } from '@nymproject/sdk-full-fat';
|
||||
import Box from '@mui/material/Box';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Button from '@mui/material/Button';
|
||||
|
||||
const nymApiUrl = 'https://validator.nymtech.net/api';
|
||||
|
||||
export const Traffic = () => {
|
||||
const [nym, setNym] = useState<NymMixnetClient>();
|
||||
const [selfAddress, setSelfAddress] = useState<string>();
|
||||
const [recipient, setRecipient] = useState<string>();
|
||||
const [payload, setPayload] = useState<Payload>();
|
||||
const [receivedMessage, setReceivedMessage] = useState<string>();
|
||||
|
||||
const init = async () => {
|
||||
const client = await createNymMixnetClient();
|
||||
setNym(client);
|
||||
|
||||
await client?.client.start({
|
||||
clientId: crypto.randomUUID(),
|
||||
nymApiUrl,
|
||||
forceTls: true, // force WSS
|
||||
});
|
||||
|
||||
client?.events.subscribeToConnected((e) => {
|
||||
const { address } = e.args;
|
||||
setSelfAddress(address);
|
||||
});
|
||||
|
||||
client?.events.subscribeToLoaded((e) => {
|
||||
console.log('Client ready: ', e.args);
|
||||
});
|
||||
|
||||
|
||||
client?.events.subscribeToTextMessageReceivedEvent((e) => {
|
||||
console.log(e.args.payload);
|
||||
setReceivedMessage(e.args.payload);
|
||||
});
|
||||
};
|
||||
|
||||
const stop = async () => {
|
||||
await nym?.client.stop();
|
||||
};
|
||||
|
||||
const send = () => nym.client.send({ payload, recipient });
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
return () => {
|
||||
stop();
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!nym || !selfAddress) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box padding={3}>
|
||||
<Paper style={{ marginTop: '1rem', padding: '2rem' }}>
|
||||
<Stack spacing={3}>
|
||||
<Typography variant="body1">My self address is:</Typography>
|
||||
<Typography variant="body1">{selfAddress || 'loading'}</Typography>
|
||||
<Typography variant="h5">Communication through the Mixnet</Typography>
|
||||
<TextField
|
||||
type="text"
|
||||
placeholder="Recipient Address"
|
||||
onChange={(e) => setRecipient(e.target.value)}
|
||||
size="small"
|
||||
/>
|
||||
<TextField
|
||||
type="text"
|
||||
placeholder="Message to send"
|
||||
multiline
|
||||
rows={4}
|
||||
onChange={(e) => setPayload({ message: e.target.value, mimeType: 'text/plain' })}
|
||||
size="small"
|
||||
/>
|
||||
<Button variant="outlined" onClick={() => send()} disabled={!payload || !recipient} sx={{width: 'fit-content'}}>
|
||||
Send
|
||||
</Button>
|
||||
</Stack>
|
||||
{receivedMessage && (
|
||||
<Stack spacing={3} style={{ marginTop: '1rem' }}>
|
||||
<Typography variant="h5">Message Received!</Typography>
|
||||
<Typography fontFamily="monospace">{receivedMessage}</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,133 @@
|
||||
```ts copy filename="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>
|
||||
);
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,189 @@
|
||||
```ts copy filename="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>
|
||||
);
|
||||
};
|
||||
|
||||
```
|
||||
@@ -0,0 +1,72 @@
|
||||
```ts copy filename="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>
|
||||
);
|
||||
};
|
||||
```
|
||||
Reference in New Issue
Block a user