started new docs draft

This commit is contained in:
mfahampshire
2024-08-26 11:53:21 +02:00
parent f1d754a1cb
commit 341337fc80
75 changed files with 4385 additions and 0 deletions
@@ -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>
);
};
```