Skip to content

Commit

Permalink
feat(wallet): unstub staking
Browse files Browse the repository at this point in the history
  • Loading branch information
mrcnk committed Dec 18, 2023
1 parent 8067883 commit 95c3336
Show file tree
Hide file tree
Showing 20 changed files with 227 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const NewAddressForm = () => {
placeholder="Name"
data-testid="newAddress__nameInput"
{...register('name')}
autoFocus
/>
<p>{errors.name?.message}</p>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const BottomNavigation = ({
variant={active ? 'secondary' : 'ghost'}
onClick={navItem.onClick}
data-testid={navItem.testId}
className={!active && 'text-muted-foreground'}
>
<navItem.icon />
</Button>
Expand Down
2 changes: 1 addition & 1 deletion packages/features/src/common/components/ViewHeading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const ViewHeading = ({
return (
<div
className={cn(
'sticky top-0 right-0 left-0 bg-background flex gap-4 items-center justify-between px-4 py-2',
'sticky top-0 right-0 left-0 bg-background flex gap-4 items-center justify-between px-4 py-2 z-10',
noHorizontalPadding && 'px-0'
)}
>
Expand Down
6 changes: 5 additions & 1 deletion packages/features/src/common/hooks/useAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export const useAccount = () => {
}),
[publicKey]
)
const stakeDelegated =
currentWallet.accountInfo.publicKey !== currentWallet.accountInfo.delegate
const copyWalletAddress = async () => {
await navigator.clipboard.writeText(publicKey ?? '')
toast({
Expand All @@ -74,9 +76,11 @@ export const useAccount = () => {
minaBalance,
gradientBackground,
copyWalletAddress,
accountInfo: currentWallet.accountInfo,
publicKey,
lockWallet,
restartCurrentWallet,
network
network,
stakeDelegated
}
}
9 changes: 9 additions & 0 deletions packages/features/src/common/hooks/useBlockchainSummary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import useSWR from 'swr'

import { fetcher } from '../lib/fetch'

export const useBlockchainSummary = () => {
return useSWR<Record<string, string>>('summary', () =>
fetcher('https://api.minaexplorer.com/summary')
)
}
5 changes: 5 additions & 0 deletions packages/features/src/common/lib/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const fetcher = async <T>(url: string) => {
const request = await fetch(url)
const body = (await request.json()) as T
return body
}
58 changes: 58 additions & 0 deletions packages/features/src/common/store/pendingTransactions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { getLocalPersistence } from '@palladxyz/persistence'
import { isBefore } from 'date-fns'
import { create } from 'zustand'
import { createJSONStorage, persist } from 'zustand/middleware'

type PendingTransaction = {
hash: string
expireAt: string
}

type PendingTransactionsState = {
pendingTransactions: PendingTransaction[]
}

type PendingTransactionsActions = {
addPendingTransaction: (pendingTransaction: PendingTransaction) => void
removePendingTransaction: (hash: string) => void
clearExpired: () => void
}

type PendingTransactionsStore = PendingTransactionsState &
PendingTransactionsActions

const initialState: PendingTransactionsState = {
pendingTransactions: []
}

export const usePendingTransactionStore = create<PendingTransactionsStore>()(
persist(
(set, get) => ({
...initialState,
addPendingTransaction: (pendingTransaction) =>
set((state) => ({
pendingTransactions: [
...state.pendingTransactions,
pendingTransaction
]
})),
removePendingTransaction: (hash) =>
set((state) => ({
pendingTransactions: state.pendingTransactions.filter(
(tx) => tx.hash !== hash
)
})),
clearExpired() {
const { pendingTransactions } = get()
const validPendingTransactions = pendingTransactions.filter(
(tx) => !isBefore(new Date(tx.expireAt), new Date())
)
return set({ pendingTransactions: validPendingTransactions })
}
}),
{
name: 'PalladPendingTransactions',
storage: createJSONStorage(getLocalPersistence)
}
)
)
6 changes: 6 additions & 0 deletions packages/features/src/common/store/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import { create } from 'zustand'

import { OutgoingTransaction } from '../types'

type TxKind = 'transaction' | 'staking'

type TransactionState = {
outgoingTransaction: OutgoingTransaction | null
kind: TxKind
}

type TransactionMutators = {
set: (outgoingTransaction: OutgoingTransaction) => void
setKind: (kind: TxKind) => void
}

type TransactionStore = TransactionState & TransactionMutators
Expand All @@ -18,9 +22,11 @@ const initialState = {

export const useTransactionStore = create<TransactionStore>()((set) => ({
...initialState,
kind: 'transaction',
set(outgoingTransaction) {
return set({ outgoingTransaction })
},
setKind: (kind) => set({ kind }),
reset() {
return set(initialState)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/features/src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export type Contact = {

export type OutgoingTransaction = {
to: string
amount: string
amount?: string
fee: string
memo: string
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export const MnemonicConfirmationView = () => {
<Input
data-testid="onboarding__mnemonicConfirmationInput"
{...register('mnemonicWord')}
autoFocus
/>
</div>
</form>
Expand Down
8 changes: 5 additions & 3 deletions packages/features/src/overview/components/OverviewCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useFiatPrice } from '@palladxyz/offchain-data'
import { CopyIcon } from 'lucide-react'
import { CopyIcon, MoveUpRightIcon, QrCodeIcon } from 'lucide-react'
import { useMemo } from 'react'
import { useNavigate } from 'react-router-dom'

Expand Down Expand Up @@ -79,18 +79,20 @@ export const OverviewCard = () => {
</div>
<div className="flex gap-2">
<Button
className="flex-1"
className="flex-1 gap-2"
size="sm"
onClick={() => navigate('/send')}
>
<MoveUpRightIcon size={16} />
Send
</Button>
<Button
variant="outline"
className="flex-1"
className="flex-1 gap-2"
size="sm"
onClick={() => navigate('/receive')}
>
<QrCodeIcon size={16} />
Receive
</Button>
</div>
Expand Down
30 changes: 23 additions & 7 deletions packages/features/src/send/components/ConfirmTransactionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { GroupedCredentials } from '@palladxyz/key-management'
import { Mina } from '@palladxyz/mina-core'
import { Multichain } from '@palladxyz/multi-chain-core'
import { useVault } from '@palladxyz/vault'
import { addHours } from 'date-fns'
import {
Payment,
SignedLegacy
Expand All @@ -12,6 +13,7 @@ import { useNavigate } from 'react-router-dom'
import { z } from 'zod'

import { useAccount } from '@/common/hooks/useAccount'
import { usePendingTransactionStore } from '@/common/store/pendingTransactions'
import { useTransactionStore } from '@/common/store/transaction'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
Expand All @@ -38,8 +40,10 @@ export const ConfirmTransactionForm = () => {
const outgoingTransaction = useTransactionStore(
(state) => state.outgoingTransaction
)
const kind = useTransactionStore((state) => state.kind)
const { addPendingTransaction } = usePendingTransactionStore()
if (!outgoingTransaction) return null
const rawAmount = parseInt(outgoingTransaction.amount || '')
const rawAmount = parseInt(outgoingTransaction.amount || '0')
const rawFee = parseFloat(outgoingTransaction.fee || '0.01')
const amount = BigInt(rawAmount * 1_000_000_000).toString()
const fee = BigInt(rawFee * 1_000_000_000).toString()
Expand All @@ -56,14 +60,19 @@ export const ConfirmTransactionForm = () => {
}
const constructedTx = await constructTx(
transaction,
Mina.TransactionKind.PAYMENT
kind === 'staking'
? Mina.TransactionKind.STAKE_DELEGATION
: Mina.TransactionKind.PAYMENT
)
const getPassphrase = async () => Buffer.from(data.spendingPassword)
const signedTx = await sign(constructedTx as any, getPassphrase)

Check warning on line 68 in packages/features/src/send/components/ConfirmTransactionForm.tsx

View workflow job for this annotation

GitHub Actions / Build and test

Unexpected any. Specify a different type
if (!signedTx) return
const submitTxArgs = {
signedTransaction: signedTx as unknown as SignedLegacy<Payment>,
kind: Mina.TransactionKind.PAYMENT,
kind:
kind === 'staking'
? Mina.TransactionKind.STAKE_DELEGATION
: Mina.TransactionKind.PAYMENT,
transactionDetails: {
fee: transaction.fee,
to: transaction.to,
Expand All @@ -74,14 +83,20 @@ export const ConfirmTransactionForm = () => {
validUntil: transaction.validUntil
}
}
const submittedTx = await submitTx(submitTxArgs as any)
console.log('submittedTx', submittedTx)
// then update account info store to get incremented nonce from network
const submittedTx = (await submitTx(submitTxArgs as any)) as any

Check warning on line 86 in packages/features/src/send/components/ConfirmTransactionForm.tsx

View workflow job for this annotation

GitHub Actions / Build and test

Unexpected any. Specify a different type

Check warning on line 86 in packages/features/src/send/components/ConfirmTransactionForm.tsx

View workflow job for this annotation

GitHub Actions / Build and test

Unexpected any. Specify a different type
addPendingTransaction({
hash: submittedTx.sendPayment.payment.hash,
expireAt: addHours(new Date(), 8).toISOString()
})
await syncWallet(
Mina.Networks.DEVNET,
currentWallet.credential.credential as GroupedCredentials
)
navigate('/transactions/success')
navigate('/transactions/success', {
state: {
hash: submittedTx.sendPayment.payment.hash
}
})
}
return (
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-4">
Expand All @@ -91,6 +106,7 @@ export const ConfirmTransactionForm = () => {
type="password"
placeholder="Spending Password"
{...register('spendingPassword')}
autoFocus
/>
</fieldset>
<Button>Send</Button>
Expand Down
10 changes: 7 additions & 3 deletions packages/features/src/send/components/SendForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const SendForm = () => {
const navigate = useNavigate()
const location = useLocation()
const setTransactionDetails = useTransactionStore((state) => state.set)
const setKind = useTransactionStore((state) => state.setKind)
const { data: accountData, isLoading: accountLoading } = useAccount()
if (accountLoading) return null
const totalBalance =
Expand Down Expand Up @@ -49,6 +50,7 @@ export const SendForm = () => {
const onSubmit = (payload: OutgoingTransaction) => {
const { fee } = getValues()
const currentFee = TransactionFee[fee]
setKind('transaction')
setTransactionDetails({
to: payload.to,
fee: String(currentFee),
Expand Down Expand Up @@ -116,15 +118,17 @@ export const SendForm = () => {
<RadioGroup defaultValue="default">
<div className="flex items-center space-x-2">
<RadioGroupItem value="flow" id="feeSlow" />
<Label htmlFor="feeSlow">Slow</Label>
<Label htmlFor="feeSlow">Slow ({TransactionFee.slow} MINA)</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="default" id="feeDefault" defaultChecked />
<Label htmlFor="feeDefault">Default</Label>
<Label htmlFor="feeDefault">
Default ({TransactionFee.default} MINA)
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="fast" id="feeFast" />
<Label htmlFor="feeFast">Fast</Label>
<Label htmlFor="feeFast">Fast ({TransactionFee.fast} MINA)</Label>
</div>
</RadioGroup>
<FormError>{errors.fee?.message}</FormError>
Expand Down
6 changes: 4 additions & 2 deletions packages/features/src/send/components/TransactionResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ export const TransactionResult = ({
return (
<div className="flex flex-col flex-1">
<ViewHeading title={title} />
<div className="flex flex-1 justify-center items-center gap-4">
<div className="flex flex-1 justify-center items-center gap-4 p-4">
<result.icon size={56} color={result.iconColor} />
<MetaField label={result.label} value={result.content} />
</div>
<Button onClick={button.onClick}>{button.label}</Button>
<div className="flex flex-col p-4">
<Button onClick={button.onClick}>{button.label}</Button>
</div>
</div>
)
}
4 changes: 3 additions & 1 deletion packages/features/src/send/views/TransactionError.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { AlertOctagonIcon } from 'lucide-react'
import { useNavigate } from 'react-router-dom'
import colors from 'tailwindcss/colors' // eslint-disable-line

import { AppLayout } from '../../common/components/AppLayout'
import { TransactionResult } from '../components/TransactionResult'

export const TransactionErrorView = () => {
const navigate = useNavigate()
return (
<AppLayout>
<TransactionResult
Expand All @@ -17,7 +19,7 @@ export const TransactionErrorView = () => {
}}
button={{
label: 'Try Again',
onClick: () => console.log('details')
onClick: () => navigate(-1)
}}
/>
</AppLayout>
Expand Down
12 changes: 5 additions & 7 deletions packages/features/src/send/views/TransactionSuccess.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { ClockIcon } from 'lucide-react'
import { useNavigate } from 'react-router-dom'
import { useLocation, useNavigate } from 'react-router-dom'
import colors from 'tailwindcss/colors' // eslint-disable-line

import { AppLayout } from '../../common/components/AppLayout'
import { TransactionResult } from '../components/TransactionResult'

export const TransactionSuccessView = () => {
const navigate = useNavigate()
const { state } = useLocation()
return (
<AppLayout>
<TransactionResult
Expand All @@ -15,14 +16,11 @@ export const TransactionSuccessView = () => {
icon: ClockIcon,
iconColor: colors.sky['500'],
label: 'Pending Transaction Hash',
content: 'Ckpa3EsAv91c4btWWbvdKNeddEEkvMxgUM5SzVBr48hNizyLrwxeN'
content: state.hash
}}
button={{
label: 'View Details',
onClick: () =>
navigate(
'/transactions/CkpZ1ckw13bPjXpdRE1F186Wu9x9HydRiW3sfgrE2JYHQxauk1sKP'
)
label: 'View Transactions',
onClick: () => navigate(`/transactions`)
}}
/>
</AppLayout>
Expand Down
Loading

0 comments on commit 95c3336

Please sign in to comment.