Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New token page #13

Merged
merged 9 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,14 @@ module.exports = {
'warn',
{ allowConstantExport: true },
],
// https://github.com/orgs/react-hook-form/discussions/8622#discussioncomment-4060570
'@typescript-eslint/no-misused-promises': [
2,
{
checksVoidReturn: {
attributes: false,
},
},
],
},
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@mantine/core": "^6.0.19",
"@mantine/form": "^6.0.19",
"@mantine/hooks": "^6.0.19",
"@mantine/notifications": "^6.0.19",
"@tabler/icons-react": "^2.30.0",
"@types/react-beautiful-dnd": "^13.1.4",
"react": "^18.2.0",
Expand Down
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '@mantine/core'
import { useLocalStorage, useHotkeys } from '@mantine/hooks'
import AppShellExample from './components/AppShell'
import { Notifications } from '@mantine/notifications'

import { AlephiumWalletProvider } from '@alephium/web3-react'
import { loadNetworkIdFromLocalStorage } from './utils/utils'
Expand Down Expand Up @@ -40,6 +41,7 @@ function App() {
withNormalizeCSS
theme={{ colorScheme }}
>
<Notifications />
<Paper>
<AppShellExample />
</Paper>
Expand Down
3 changes: 2 additions & 1 deletion src/components/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ImportMultisig from './Multisig/ImportMultisig'
import ShowMultiSig from './Multisig/ShowMultisig'
import SignMultisigTx from './Multisig/SignMultisigTx'
import BuildMultisigTx from './Multisig/BuildMultisigTx'
import NewToken from './Token/NewToken'

function AppShellExample() {
const theme = useMantineTheme()
Expand Down Expand Up @@ -41,7 +42,7 @@ function AppShellExample() {
<Route path="/" element={<WalletInfo />} />
<Route path="/token/all" element={<WIP />} />
<Route path="/token/info" element={<TokenInfo />} />
<Route path="/token/new" element={<WIP />} />
<Route path="/token/new" element={<NewToken />} />
<Route path="/token/burn" element={<WIP />} />
<Route path="/nft/all" element={<WIP />} />
<Route path="/nft/info" element={<WIP />} />
Expand Down
7 changes: 6 additions & 1 deletion src/components/Multisig/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ export const defaultNewMultisigTx = {
multisig: '',
signers: [] as string[],
destinations: [
{ address: '', symbol: '', tokenId: '', tokenAmount: undefined as number | undefined },
{
address: '',
symbol: '',
tokenId: '',
tokenAmount: undefined as number | undefined,
},
],
sweep: undefined as boolean | undefined,
unsignedTx: undefined as string | undefined,
Expand Down
13 changes: 13 additions & 0 deletions src/components/NoWalletAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Alert } from '@mantine/core'
import { IconInfoCircle } from '@tabler/icons-react'

export const NoWalletAlert = () => (
<Alert
icon={<IconInfoCircle />}
variant="light"
color="blue"
title="No wallet connected"
>
You must connect a wallet with funds to perform this operation.
</Alert>
)
180 changes: 114 additions & 66 deletions src/components/Token/NewToken.tsx
Original file line number Diff line number Diff line change
@@ -1,98 +1,146 @@
import { useForm } from '@mantine/form'
import { useForm, isInRange, hasLength } from '@mantine/form'
import {
TextInput,
Text,
Button,
Group,
Box,
Center,
rem,
Stack,
NumberInput,
Space,
Title,
Paper,
} from '@mantine/core'
import { useWallet, useBalance } from '@alephium/web3-react'
import { useWallet } from '@alephium/web3-react'
import { deployNewToken } from './shared'
import { useExplorerFE, useNetworkId } from '../../utils/utils'
import { IconExternalLink, IconCheck, IconX } from '@tabler/icons-react'
import { useState } from 'react'
import { notifications } from '@mantine/notifications'
import { NoWalletAlert } from '../NoWalletAlert'

interface NewTokenSchema {
name: string
symbol: string
decimals: number
supply: number
}

function NewToken() {
const wallet = useWallet()
const form = useForm({
const [isSubmitting, setIsSubmitting] = useState(false)
const { signer } = useWallet()
const [networkId] = useNetworkId()
const explorerUrl = useExplorerFE()
const form = useForm<NewTokenSchema>({
initialValues: {
name: '',
symbol: '',
decimals: 18,
supply: 1000_000,
},

validate: {
name: (value) =>
value.length < 3 ? 'Name must have at least 3 letters' : null,
name: hasLength({ min: 3 }, 'Name must have 3 or more characters'),
symbol: (value) =>
/[A-Z]{3,6}/.test(value) ? null : 'Symbol must be 3-6 capital letters',
decimals: (value) =>
value < 0 || value > 18 ? 'Decimals must be between 0 and 18' : null,
supply: (value) =>
value < 0 || !Number.isInteger(value)
? 'Supply must be positive integer'
: null,
decimals: isInRange(
{ min: 0, max: 18 },
'Decimals must be between 0 and 18'
),
supply: isInRange({ min: 1 }, 'Supply must be a positive integer'),
},
})
const { balance, updateBalanceForTx } = useBalance()

console.log(`==== `, balance, updateBalanceForTx)
const onSuccess = (txId: string) => {
const txUrl = `${explorerUrl}/transactions/${txId}`

notifications.show({
title: 'Tokens successfully issued!',
message: (
<span>
View your minting transaction in the explorer{' '}
<a href={txUrl} target="_blank" rel="noreferrer">
here <IconExternalLink size={12} />.
</a>
</span>
),
icon: <IconCheck size="1.1rem" />,
color: 'teal',
})
}

const onError = (e: Error) => {
console.error(e)
notifications.show({
title: 'Failed to issue tokens',
message: `Error: ${e.message}`,
icon: <IconX size="1.1rem" />,
color: 'red',
})
}

if (wallet === undefined || wallet.signer === undefined) {
return <></>
const onSubmit = (values: NewTokenSchema) => {
setIsSubmitting(true)
deployNewToken(networkId, signer!, values)
.then(onSuccess)
.catch(onError)
.finally(() => setIsSubmitting(false))
}

return (
<Center h={rem('80%')}>
<Stack>
<Box ta="left" w="40rem" mx="auto">
<TextInput
label="Token Name"
placeholder="Token Name"
{...form.getInputProps('name')}
/>
<TextInput
mt="md"
label="Token Symbol (3-6 capital letters)"
placeholder="Token Symbol"
{...form.getInputProps('symbol')}
/>
<TextInput
mt="md"
label="Decimals"
placeholder="Decimals"
{...form.getInputProps('decimals')}
/>
<NumberInput
mt="md"
label="Token Supply"
placeholder="supply"
hideControls
{...form.getInputProps('supply')}
/>
<TextInput
mt="md"
label="Owner"
placeholder=""
value={wallet.account?.address}
disabled
/>
</Box>

<Group position="center" mt="xl">
<Button
variant="outline"
onClick={() => {
// deployNewToken(context.signerProvider!, form.values)
updateBalanceForTx(
'fe9ede54e19411ccdbaaa620e1249fffeeb494266c26d2a9d1e6e6aec16d0bfe'
)
}}
>
Create
</Button>
</Group>
</Stack>
<Paper shadow="xs" p="md">
<Stack spacing="md">
<Box ta="left" w="40rem" mx="auto">
{!signer && <NoWalletAlert />}
<Title order={1}>Issue new tokens</Title>
<Text size="xs" c="dimmed">
Newly created tokens will be owned by your currently connected
wallet account.
</Text>
<Space h="md" />
<form onSubmit={form.onSubmit(onSubmit)}>
<TextInput
label="Token Name"
withAsterisk
placeholder="Token Name"
{...form.getInputProps('name')}
/>
<TextInput
mt="md"
label="Token Symbol"
withAsterisk
description="Symbol must be 3-6 capital letters"
placeholder="Token Symbol"
{...form.getInputProps('symbol')}
/>
<NumberInput
mt="md"
label="Decimals"
withAsterisk
description="Must be in the range of 0-18"
placeholder="Decimals"
hideControls
{...form.getInputProps('decimals')}
/>
<NumberInput
mt="md"
label="Token Supply"
withAsterisk
placeholder="supply"
hideControls
{...form.getInputProps('supply')}
/>
<Group position="right" mt="xl">
<Button type="submit" disabled={!signer} loading={isSubmitting}>
Issue Tokens
</Button>
</Group>
</form>
</Box>
</Stack>
</Paper>
</Center>
)
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Token/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ export async function deployNewToken(
decimals,
supply,
}: { name: string; symbol: string; decimals: number; supply: number }
) {
): Promise<string> {
const deployments = loadDeployments(network)
console.log(await signer.getSelectedAccount())
const result = await DeployNewToken.execute(signer, {
initialFields: {
templateId: deployments.contracts.SimpleToken.contractInstance.contractId,
Expand All @@ -26,4 +25,5 @@ export async function deployNewToken(
attoAlphAmount: ONE_ALPH,
})
console.log(result)
return result.txId
}
9 changes: 8 additions & 1 deletion src/components/Wallet/WalletInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ function WalletInfo() {
const { account, connectionStatus } = useWallet()
const { network, addressGroup, keyType } = useWalletConfig()

console.log(`WalletInfo:`, connectionStatus, network, addressGroup, keyType, account)
console.log(
`WalletInfo:`,
connectionStatus,
network,
addressGroup,
keyType,
account
)

if (connectionStatus === 'connecting' && !account) return null
if (connectionStatus === 'disconnected') return <NoWallet />
Expand Down
8 changes: 6 additions & 2 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,13 @@ export function useExplorerFE(): string {
: devnet_explorer_url
}

export function useTokenList(): TokenInfo[] {
export function useTokenList(): TokenInfo[] {
const [network] = useNetworkId()
return network === 'mainnet' ? mainnetTokensMetadata.tokens : network === 'testnet' ? testnetTokensMetadata.tokens : []
return network === 'mainnet'
? mainnetTokensMetadata.tokens
: network === 'testnet'
? testnetTokensMetadata.tokens
: []
}

export function getTokenMetadata(network: 'mainnet' | 'testnet'): TokenList {
Expand Down
Loading
Loading