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

Setup launch token form #14

Merged
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
6 changes: 2 additions & 4 deletions frontend/craco.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ const shouldLintOrTypeCheck = !isProduction

module.exports = {
babel: {
plugins: [
'@vanilla-extract/babel-plugin',
],
plugins: ['@vanilla-extract/babel-plugin'],
},
eslint: {
enable: shouldLintOrTypeCheck,
Expand Down Expand Up @@ -84,7 +82,7 @@ module.exports = {
{
loader: require.resolve('css-loader'),
options: {
url: false // Required as image imports should be handled via JS/TS import statements
url: false, // Required as image imports should be handled via JS/TS import statements
},
},
],
Expand Down
7 changes: 6 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@
"lint": "eslint ."
},
"dependencies": {
"@starknet-react/core": "^0.14.8",
"@hookform/resolvers": "^3.3.2",
"@starknet-react/chains": "^0.1.0",
"@starknet-react/core": "^2.0.0",
"@types/react-dom": "^18.2.1",
"@vanilla-extract/css": "^1.11.0",
"@vanilla-extract/recipes": "^0.4.0",
"@vanilla-extract/sprinkles": "^1.6.0",
"clsx": "^1.2.1",
"lucide-react": "^0.294.0",
"ms.macro": "^2.0.0",
"polished": "^4.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.49.0",
"react-router-dom": "^6.11.0",
"starknet": "^5.10.0",
"ua-parser-js": "^1.0.35",
"zod": "^3.22.4",
"zustand": "^4.3.8"
},
"devDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ interface SecondaryButtonProps extends ButtonProps {
export const SecondaryButton = ({ className, withIcon, ...props }: SecondaryButtonProps) => (
<Box as="button" className={clsx(className, styles.secondaryButton({ withIcon }))} {...props} />
)

export const IconButton = ({ className, ...props }: ButtonProps) => (
<Box as="button" className={clsx(className, styles.iconButton)} {...props} />
)
25 changes: 25 additions & 0 deletions frontend/src/components/Button/style.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export const Base = sprinkles({
fontSize: '16',
color: 'text1',
paddingY: '12',
pointerEvents: {
default: 'all',
disabled: 'none',
},
})

export const primaryButton = style([
Expand Down Expand Up @@ -65,3 +69,24 @@ export const secondaryButton = recipe({
withIcon: false,
},
})

export const iconButton = style([
sprinkles({
paddingX: '2',
paddingY: '2',
fontSize: '14',
borderRadius: '10',
fontWeight: 'medium',
cursor: 'pointer',
pointerEvents: {
default: 'all',
disabled: 'none',
},
color: {
default: 'text2',
hover: 'text1',
},
background: 'transparent',
border: 'none',
}),
])
15 changes: 12 additions & 3 deletions frontend/src/components/Input/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import clsx from 'clsx'
import { forwardRef } from 'react'
import Box, { BoxProps } from 'src/theme/components/Box'

import * as styles from './style.css'

export default function Input({ className, ...props }: BoxProps) {
type InputProps = {
addon?: React.ReactNode
} & BoxProps

const Input = forwardRef(function ({ addon, className, ...props }: InputProps, ref) {
return (
<Box className={clsx(className, styles.inputContainer)}>
<Box as="input" className={styles.input} {...props} />
<Box as="input" className={styles.input} {...props} ref={ref} />
{addon}
</Box>
)
}
})

Input.displayName = 'Input'
export default Input
6 changes: 3 additions & 3 deletions frontend/src/components/WalletModal/Option.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useConnectors } from '@starknet-react/core'
import { useConnect } from '@starknet-react/core'
import { Connection, L2Connection } from 'src/connections'
import { Row } from 'src/theme/components/Flex'
import * as Text from 'src/theme/components/Text'
Expand All @@ -25,8 +25,8 @@ interface L2OptionProps {

export function L2Option({ connection }: L2OptionProps) {
// wallet activation
const { connect } = useConnectors()
const activate = () => connect(connection.connector)
const { connect } = useConnect()
const activate = () => connect({ connector: connection.connector })

return <Option connection={connection} activate={activate} />
}
4 changes: 2 additions & 2 deletions frontend/src/components/WalletModal/Overview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useConnectors, useNetwork } from '@starknet-react/core'
import { useDisconnect, useNetwork } from '@starknet-react/core'
import { useCallback } from 'react'
import { useCloseModal, useL2WalletOverviewModal } from 'src/hooks/useModal'

Expand Down Expand Up @@ -38,7 +38,7 @@ export function L2WalletOverviewModal() {
const [isOpen] = useL2WalletOverviewModal()

// disconnect
const { disconnect } = useConnectors()
const { disconnect } = useDisconnect()

// chain infos
const { chain } = useNetwork()
Expand Down
16 changes: 8 additions & 8 deletions frontend/src/components/Web3Provider/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { InjectedConnector, StarknetConfig } from '@starknet-react/core'
import { useMemo } from 'react'
import { getL2Connections } from 'src/connections'
import { sepolia } from '@starknet-react/chains'
import { argent, braavos, publicProvider, StarknetConfig, useInjectedConnectors } from '@starknet-react/core'

// STARKNET

Expand All @@ -9,13 +8,14 @@ interface StarknetProviderProps {
}

export function StarknetProvider({ children }: StarknetProviderProps) {
const connections = getL2Connections()
const connectors: InjectedConnector[] = connections.map(({ connector }) => connector)

const key = useMemo(() => connections.map((connection) => connection.getName()).join('-'), [connections])
const { connectors } = useInjectedConnectors({
recommended: [argent(), braavos()],
includeRecommended: 'onlyIfNoConnectors',
order: 'random',
})

return (
<StarknetConfig connectors={connectors} key={key} autoConnect>
<StarknetConfig connectors={connectors} chains={[sepolia]} provider={publicProvider()} autoConnect>
{children}
</StarknetConfig>
)
Expand Down
131 changes: 124 additions & 7 deletions frontend/src/pages/Launch/index.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,152 @@
import { PrimaryButton } from 'src/components/Button'
import { zodResolver } from '@hookform/resolvers/zod'
import { useAccount } from '@starknet-react/core'
import { Wallet, X } from 'lucide-react'
import { useCallback } from 'react'
import { useFieldArray, useForm } from 'react-hook-form'
import { IconButton, PrimaryButton, SecondaryButton } from 'src/components/Button'
import Input from 'src/components/Input'
import Box from 'src/theme/components/Box'
import { Column, Row } from 'src/theme/components/Flex'
import * as Text from 'src/theme/components/Text'
import { z } from 'zod'

import * as styles from './style.css'

const MAX_HOLDERS = 10

const address = z.string().refine(
(addr) => {
return addr.startsWith('0x') && addr.length === 66
},
{ message: 'Invalid Starknet address' }
)

const holder = z.object({
address,
amount: z.number().min(0),
})

const schema = z.object({
name: z.string().min(1),
symbol: z.string().min(1),
initialRecipientAddress: address,
ownerAddress: address,
holders: z.array(holder),
})

export default function HomePage() {
const { account, address } = useAccount()

const {
control,
register,
handleSubmit,
setValue,
formState: { errors },
} = useForm<z.infer<typeof schema>>({ resolver: zodResolver(schema) })

const { fields, append, remove } = useFieldArray({
control,
name: 'holders',
})

const deployToken = useCallback(
(data: z.infer<typeof schema>) => {
console.log('deploy token form account', account?.address, data)
// data contains data to be used for deploying.
// use `account.execute` to deploy the token.
// TODO: wait for Starknet React to support overriding call arguments so it
// plays nicely with react-hook-form.
},
[account]
)

return (
<Row className={styles.wrapper}>
<Box className={styles.container}>
<Box as="form">
<Box as="form" onSubmit={handleSubmit(deployToken)}>
<Column gap="20">
<Column gap="4">
<Text.Body className={styles.inputLabel}>Name</Text.Body>
<Input placeholder="Dogecoin" />
<Input placeholder="Dogecoin" {...register('name')} />
<Box className={styles.errorContainer}>
{errors.name?.message ? <Text.Error>{errors.name.message}</Text.Error> : null}
</Box>
</Column>

<Column gap="4">
<Text.Body className={styles.inputLabel}>Symbol</Text.Body>
<Input placeholder="DOGE" />
<Input placeholder="DOGE" {...register('symbol')} />
<Box className={styles.errorContainer}>
{errors.symbol?.message ? <Text.Error>{errors.symbol.message}</Text.Error> : null}
</Box>
</Column>

<Column gap="4">
<Text.Body className={styles.inputLabel}>Decimals</Text.Body>
<Input placeholder="18" />
<Text.Body className={styles.inputLabel}>Initial Recipient Address</Text.Body>
<Input
addon={
<IconButton
disabled={!address}
onClick={() => (address ? setValue('initialRecipientAddress', address) : null)}
>
<Wallet />
</IconButton>
}
{...register('initialRecipientAddress')}
/>
<Box className={styles.errorContainer}>
{errors.initialRecipientAddress?.message ? (
<Text.Error>{errors.initialRecipientAddress.message}</Text.Error>
) : null}
</Box>
</Column>

<Column gap="4">
<Text.Body className={styles.inputLabel}>Owner Address</Text.Body>
<Input
addon={
<IconButton disabled={!address} onClick={() => (address ? setValue('ownerAddress', address) : null)}>
<Wallet />
</IconButton>
}
{...register('ownerAddress')}
/>
<Box className={styles.errorContainer}>
{errors.ownerAddress?.message ? <Text.Error>{errors.ownerAddress.message}</Text.Error> : null}
</Box>
</Column>

{fields.map((field, index) => (
<Column gap="4" key={field.id}>
<Text.Body className={styles.inputLabel}>Holder {index + 1}</Text.Body>
<Column gap="2" flexDirection="row">
<Input placeholder="Holder address" {...register(`holders.${index}.address`)} />
<Input placeholder="Tokens" {...register(`holders.${index}.amount`, { valueAsNumber: true })} />
<IconButton onClick={() => remove(index)}>
<X />
</IconButton>
</Column>
<Box className={styles.errorContainer}>
{errors.holders?.[index]?.address?.message ? (
<Text.Error>{errors.holders?.[index]?.address?.message}</Text.Error>
) : null}
{errors.holders?.[index]?.amount?.message ? (
<Text.Error>{errors.holders?.[index]?.amount?.message}</Text.Error>
) : null}
</Box>
</Column>
))}

<SecondaryButton disabled={fields.length >= MAX_HOLDERS} onClick={() => append({ address: '', amount: 0 })}>
Add holder
</SecondaryButton>

<div />

<PrimaryButton className={styles.deployButton}>DEPLOY</PrimaryButton>
<PrimaryButton disabled={!account} className={styles.deployButton}>
{account ? 'DEPLOY' : 'CONNECT WALLET'}
</PrimaryButton>
</Column>
</Box>
</Box>
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/pages/Launch/style.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { vars } from 'src/theme/css/vars.css'

export const wrapper = style([
{
padding: '68px 8px 0',
padding: '68px 0px',
},
sprinkles({
width: 'full',
Expand All @@ -27,6 +27,9 @@ export const container = style([
export const deployButton = style([
{
background: vars.color.vibrantGradient,
':disabled': {
opacity: '0.5',
},
},
sprinkles({
fontSize: '24',
Expand All @@ -38,3 +41,9 @@ export const inputLabel = sprinkles({
marginLeft: '8',
fontWeight: 'medium',
})

export const errorContainer = sprinkles({
paddingX: '8',
paddingTop: '4',
color: 'vibrantRed',
})
14 changes: 14 additions & 0 deletions frontend/src/theme/components/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,17 @@ export const HeadlineLarge = ({ className, ...props }: TextProps) => (
{...props}
/>
)

export const Error = ({ className, ...props }: TextProps) => (
<TextWrapper
className={clsx(
className,
sprinkles({
fontWeight: 'normal',
color: 'vibrantRed',
fontSize: '14',
})
)}
{...props}
/>
)
2 changes: 2 additions & 0 deletions frontend/src/theme/css/sprinkles.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,12 @@ const unresponsiveProperties = defineProperties({
hover: { selector: '&:hover' },
active: { selector: '&:active' },
before: { selector: '&:before' },
disabled: { selector: '&:disabled' },
},
defaultCondition: 'default',
properties: {
cursor: ['default', 'pointer', 'auto'],
pointerEvents: ['none', 'all'],

borderStyle,
borderLeftStyle: borderStyle,
Expand Down
Loading