Skip to content

Commit

Permalink
Merge pull request #94 from palladians/feat/tx-sending
Browse files Browse the repository at this point in the history
chore(update transaction flow in UI)
  • Loading branch information
mrcnk authored Dec 16, 2023
2 parents 0a5ed78 + acd82a4 commit 09f47da
Show file tree
Hide file tree
Showing 21 changed files with 1,035 additions and 381 deletions.
4 changes: 2 additions & 2 deletions apps/extension/.env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
VITE_APP_MODE=web
VITE_APP_DEFAULT_NETWORK=Mainnet
VITE_APP_DEFAULT_NETWORK=Devnet

VITE_APP_MINA_PROXY_MAINNET_URL=https://proxy.minaexplorer.com/
VITE_APP_MINA_EXPLORER_MAINNET_URL=https://graphql.minaexplorer.com/
VITE_APP_MINA_EXPLORER_MAINNET_URL=https://graphql.minaexplorer.com/graphql

VITE_APP_MINA_PROXY_DEVNET_URL=https://proxy.devnet.minaexplorer.com/
VITE_APP_MINA_EXPLORER_DEVNET_URL=https://devnet.graphql.minaexplorer.com
Expand Down
20 changes: 10 additions & 10 deletions apps/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"zod": "^3.21.4"
},
"devDependencies": {
"@crxjs/vite-plugin": "^2.0.0-beta.18",
"@crxjs/vite-plugin": "^2.0.0-beta.21",
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
"@originjs/vite-plugin-commonjs": "^1.0.3",
Expand All @@ -45,17 +45,17 @@
"@types/webextension-polyfill": "^0.10.0",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"@vitejs/plugin-react-swc": "^3.3.2",
"@vitejs/plugin-react-swc": "^3.5.0",
"path": "^0.12.7",
"rollup-plugin-node-polyfills": "^0.2.1",
"rollup-plugin-polyfill-node": "^0.12.0",
"vite": "^4.3.9",
"vite-plugin-commonjs": "^0.7.1",
"vite-plugin-node-polyfills": "^0.9.0",
"vite-plugin-require-transform": "^1.0.20",
"vite-plugin-svgr": "^3.2.0",
"vite-plugin-top-level-await": "^1.3.1",
"vite-plugin-wasm": "^3.2.2",
"web-ext": "^7.6.2"
"vite": "^4.5.1",
"vite-plugin-commonjs": "^0.10.1",
"vite-plugin-node-polyfills": "^0.17.0",
"vite-plugin-require-transform": "^1.0.21",
"vite-plugin-svgr": "^4.2.0",
"vite-plugin-top-level-await": "^1.4.1",
"vite-plugin-wasm": "^3.3.0",
"web-ext": "^7.9.0"
}
}
3 changes: 2 additions & 1 deletion packages/features/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@
"esbuild-plugin-polyfill-node": "^0.3.0",
"esbuild-plugin-svgr": "^2.0.0",
"graphql-request": "^6.1.0",
"mina-signer": "^2.1.1",
"swr": "^2.1.5",
"vite": "^4.3.9",
"vite": "^4.5.1",
"vite-plugin-svgr": "^3.2.0",
"vite-plugin-top-level-await": "^1.3.1"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/features/src/common/hooks/useAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const useAccount = () => {
)
const rawMinaBalance = swr.isLoading
? 0
: swr.data?.accountInfo.balance.total || 0
: swr.data?.accountInfo?.balance?.total ?? 0
const minaBalance =
rawMinaBalance && BigInt(rawMinaBalance) / BigInt(1_000_000_000)
const gradientBackground = useMemo(
Expand All @@ -46,7 +46,7 @@ export const useAccount = () => {
[publicKey]
)
const copyWalletAddress = async () => {
await navigator.clipboard.writeText(publicKey || '')
await navigator.clipboard.writeText(publicKey ?? '')
toast({
title: 'Wallet address was copied.'
})
Expand Down
7 changes: 6 additions & 1 deletion packages/features/src/common/hooks/useTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import { useAppStore } from '../store/app'

export const useTransaction = ({ hash }: { hash: string }) => {
const currentWallet = useVault((state) => state.getCurrentWallet())
const _syncTransactions = useVault((state) => state._syncTransactions)
const getTransaction = useVault((state) => state.getTransaction)
const { publicKey } = currentWallet.accountInfo
const network = useAppStore((state) => state.network)
const syncAndGetTransaction = async () => {
await _syncTransactions(network, currentWallet?.credential.credential)
return getTransaction(network, publicKey, hash)
}
return useSWR(
publicKey
? [
Expand All @@ -17,6 +22,6 @@ export const useTransaction = ({ hash }: { hash: string }) => {
Mina.Networks[network.toUpperCase() as keyof typeof Mina.Networks]
]
: null,
() => getTransaction(network, publicKey, hash)
async () => await syncAndGetTransaction()
)
}
7 changes: 6 additions & 1 deletion packages/features/src/common/hooks/useTransactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import { useAppStore } from '../store/app'

export const useTransactions = () => {
const currentWallet = useVault((state) => state.getCurrentWallet())
const _syncTransactions = useVault((state) => state._syncTransactions)
const getTransactions = useVault((state) => state.getTransactions)
const { publicKey } = currentWallet.accountInfo
const network = useAppStore((state) => state.network)
const syncAndGetTransactions = async () => {
await _syncTransactions(network, currentWallet?.credential.credential)
return getTransactions(network, publicKey)
}
return useSWR(
publicKey
? [
Expand All @@ -17,6 +22,6 @@ export const useTransactions = () => {
Mina.Networks[network.toUpperCase() as keyof typeof Mina.Networks]
]
: null,
() => getTransactions(network, publicKey)
async () => await syncAndGetTransactions()
)
}
11 changes: 11 additions & 0 deletions packages/features/src/components/list-skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Skeleton } from './ui/skeleton'

export const ListSkeleton = () => {
return (
<div className="flex flex-col gap-4">
<Skeleton className="w-full h-8" />
<Skeleton className="w-full h-8" />
<Skeleton className="w-full h-8" />
</div>
)
}
4 changes: 2 additions & 2 deletions packages/features/src/receive/views/Receive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useAccount } from '../../common/hooks/useAccount'
export const ReceiveView = () => {
const { theme } = useTheme()
const navigate = useNavigate()
const { copyWalletAddress, publicKey } = useAccount()
const { copyWalletAddress, publicKey, gradientBackground } = useAccount()
return (
<AppLayout>
<div className="flex flex-col flex-1 gap-4">
Expand All @@ -36,7 +36,7 @@ export const ReceiveView = () => {
/>
<div
className="absolute w-full h-full inset-0 dark:mix-blend-darken mix-blend-lighten"
style={{ backgroundImage: '' }}
style={{ backgroundImage: gradientBackground }}
/>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { z } from 'zod'

export const ConfirmTransactionSchema = z.object({
spendingPassword: z.string().min(1)
})
92 changes: 92 additions & 0 deletions packages/features/src/send/components/ConfirmTransactionForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { Mina } from '@palladxyz/mina-core'
import { Multichain } from '@palladxyz/multi-chain-core'
import { useVault } from '@palladxyz/vault'
import {
Payment,
SignedLegacy
} from 'mina-signer/dist/node/mina-signer/src/TSTypes'
import { SubmitHandler, useForm } from 'react-hook-form'
import { useNavigate } from 'react-router-dom'
import { z } from 'zod'

import { useAccount } from '@/common/hooks/useAccount'
import { useTransactionStore } from '@/common/store/transaction'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'

import { ConfirmTransactionSchema } from './ConfirmTransactionForm.schema'

type ConfirmTransactionData = z.infer<typeof ConfirmTransactionSchema>

export const ConfirmTransactionForm = () => {
const navigate = useNavigate()
const sign = useVault((state) => state.sign)
const submitTx = useVault((state) => state.submitTx)
const constructTx = useVault((state) => state.constructTx)
const currentWallet = useVault((state) => state.getCurrentWallet())
const { publicKey } = useAccount()
const { register, handleSubmit } = useForm<ConfirmTransactionData>({
resolver: zodResolver(ConfirmTransactionSchema),
defaultValues: {
spendingPassword: ''
}
})
const outgoingTransaction = useTransactionStore(
(state) => state.outgoingTransaction
)
if (!outgoingTransaction) return null
const rawAmount = parseInt(outgoingTransaction.amount || '')
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()
const onSubmit: SubmitHandler<ConfirmTransactionData> = async (data) => {
const transaction: Multichain.MultiChainTransactionBody = {
to: outgoingTransaction.to,
from: publicKey,
memo: outgoingTransaction.memo,
validUntil: '4294967295',
fee,
amount,
nonce: currentWallet.accountInfo.inferredNonce,
type: 'payment'
}
const constructedTx = await constructTx(
transaction,
Mina.TransactionKind.PAYMENT
)
const getPassphrase = async () => Buffer.from(data.spendingPassword)
const signedTx = await sign(constructedTx as any, getPassphrase)

Check warning on line 60 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,
transactionDetails: {
fee: transaction.fee,
to: transaction.to,
from: transaction.from,
nonce: transaction.nonce,
memo: transaction.memo,
amount: transaction.amount,
validUntil: transaction.validUntil
}
}
const submittedTx = await submitTx(submitTxArgs as any)

Check warning on line 75 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
console.log('submittedTx', submittedTx)
navigate('/transactions/success')
}
return (
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-4">
<fieldset className="flex flex-col gap-2">
<Label>Spending Password</Label>
<Input
type="password"
placeholder="Spending Password"
{...register('spendingPassword')}
/>
</fieldset>
<Button>Send</Button>
</form>
)
}
38 changes: 5 additions & 33 deletions packages/features/src/send/views/TransactionSummary.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { Mina } from '@palladxyz/mina-core'
import { Multichain } from '@palladxyz/multi-chain-core'
import { useVault } from '@palladxyz/vault'
import { ArrowDownLeftIcon } from 'lucide-react'
import { useMemo } from 'react'
import { useNavigate } from 'react-router-dom'

import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'

import { AppLayout } from '../../common/components/AppLayout'
Expand All @@ -14,12 +10,10 @@ import { ViewHeading } from '../../common/components/ViewHeading'
import { useAccount } from '../../common/hooks/useAccount'
import { truncateString } from '../../common/lib/string'
import { useTransactionStore } from '../../common/store/transaction'
import { ConfirmTransactionForm } from '../components/ConfirmTransactionForm'

export const TransactionSummaryView = () => {
const navigate = useNavigate()
const sign = useVault((state) => state.sign)
const submitTx = useVault((state) => state.submitTx)
const constructTx = useVault((state) => state.constructTx)
const { publicKey } = useAccount()
if (!publicKey) return null
const outgoingTransaction = useTransactionStore(
Expand All @@ -30,32 +24,10 @@ export const TransactionSummaryView = () => {
() =>
outgoingTransaction?.amount &&
outgoingTransaction?.fee &&
outgoingTransaction?.amount + outgoingTransaction.fee,
parseFloat(outgoingTransaction?.amount) +
parseFloat(outgoingTransaction.fee),
[]
)
const rawAmount = parseInt(outgoingTransaction.amount || '')
const rawFee = parseInt(outgoingTransaction.fee || '')
const amount = BigInt(rawAmount * 1_000_000_000)
const fee = BigInt(rawFee * 1_000_000_000)
const constructAndSubmitTx = async () => {
const transaction: Multichain.MultiChainTransactionBody = {
to: outgoingTransaction.to,
from: publicKey,
fee,
amount,
nonce: 0, // TODO: nonce management -- should we have a Nonce Manager in the wallet? Yes.
type: 'payment' // TODO: handle with enums (payment, delegation, zkApp commands?)
}
const constructedTx = await constructTx(
transaction,
Mina.TransactionKind.PAYMENT
)
const signedTx = await sign(constructedTx as any) // TODO: Fix this with new wallet API
if (!signedTx) return
const submittedTx = await submitTx(signedTx as any)
console.log('>>>ST', submittedTx)
navigate('/transactions/success')
}
return (
<AppLayout>
<div className="flex flex-1 flex-col gap-4">
Expand Down Expand Up @@ -93,7 +65,7 @@ export const TransactionSummaryView = () => {
/>
</Card>
<Card className="grid grid-cols-2 gap-4 p-2">
{/*<MetaField label="Kind" value={outgoingTransaction.kind} />*/}
{/* <MetaField label="Kind" value={outgoingTransaction.kind} /> */}
{outgoingTransaction.amount && (
<MetaField
label="Amount"
Expand All @@ -106,7 +78,7 @@ export const TransactionSummaryView = () => {
)}
</Card>
<div className="flex-1" />
<Button onClick={constructAndSubmitTx}>Send</Button>
<ConfirmTransactionForm />
</div>
</AppLayout>
)
Expand Down
10 changes: 8 additions & 2 deletions packages/features/src/transactions/views/Transactions.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import { useNavigate } from 'react-router-dom'

import { ListSkeleton } from '@/components/list-skeleton'

import { AppLayout } from '../../common/components/AppLayout'
import { ViewHeading } from '../../common/components/ViewHeading'
import { useTransactions } from '../../common/hooks/useTransactions'
import { TransactionsList } from '../components/TransactionsList'

export const TransactionsView = () => {
const navigate = useNavigate()
const { data: transactions } = useTransactions()
const { data: transactions, isLoading } = useTransactions()
return (
<AppLayout>
<div className="flex flex-col flex-1 gap-4">
<ViewHeading
title="Transactions"
button={{ label: 'Send', onClick: () => navigate('/send') }}
/>
{transactions && <TransactionsList transactions={transactions} />}
{isLoading ? (
<ListSkeleton />
) : (
transactions && <TransactionsList transactions={transactions} />
)}
</div>
</AppLayout>
)
Expand Down
6 changes: 6 additions & 0 deletions packages/key-management/src/InMemoryKeyAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,10 @@ export class InMemoryKeyAgent extends KeyAgentBase implements KeyAgent {
await this.deriveCredentials(payload, args, getPassphrase, false)
return this
}

getSeralizableData(): SerializableInMemoryKeyAgentData {
return {
...this.serializableData
}
}
}
2 changes: 1 addition & 1 deletion packages/persistence/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"devDependencies": {
"@capacitor/core": "^5.2.0",
"@palladxyz/common": "*",
"vite": "^4.3.9",
"vite": "^4.5.1",
"zustand": "^4.4.7"
}
}
Loading

0 comments on commit 09f47da

Please sign in to comment.