redesign fixes (#160)

* fix: show error on invalid transactions

* fix: leave nonce unset if not changed by the user

Co-authored-by: Carlo Sala <carlosalag@protonmail.com>

* avoid spamming nonce requests

---------

Co-authored-by: Carlo Sala <carlosalag@protonmail.com>
This commit is contained in:
Victor Oliva
2026-06-25 10:57:35 +02:00
committed by GitHub
parent d7ec471a14
commit 2e90d3f9b0
3 changed files with 70 additions and 65 deletions

View File

@@ -116,10 +116,18 @@ const ExtrinsicsWorkspaceEntry: FC<{ event: TrackedTransactionEvent }> = ({
</div>
<div className="contents">
<dt className="text-muted-foreground">Hash</dt>
<dd className="truncate font-mono flex items-center gap-1">
{event.txHash ? shortStr(event.txHash, 8) : ""}
<CopyText text={event.txHash} disabled={!event.txHash} size={14} />
</dd>
{event.txHash ? (
<dd className="truncate font-mono flex items-center gap-1">
{shortStr(event.txHash, 8)}
<CopyText
text={event.txHash}
disabled={!event.txHash}
size={14}
/>
</dd>
) : (
<dd />
)}
</div>
{"block" in event ? (
<div className="contents">
@@ -159,11 +167,11 @@ const ExtrinsicsWorkspaceEntry: FC<{ event: TrackedTransactionEvent }> = ({
</dd>
</div>
) : null}
{event.type === "error" ? (
{event.type === "error" || event.type === "invalid" ? (
<div className="contents">
<dt className="text-muted-foreground">Error</dt>
<dd className="truncate font-bold font-mono text-red-500">
{JSON.stringify(event.value, jsonSerialize)}
<dd className="text-xs">
<JsonDisplay src={event.value?.error ?? event.value} />
</dd>
</div>
) : null}

View File

@@ -12,7 +12,11 @@ import {
import { createState } from "@/lib/externalState"
import { PolkahubModalBasedManagers } from "@/pages/Accounts/Providers"
import { client$, unsafeApi$ } from "@/state/chains/chain.state"
import { selectedAccount$ } from "@/state/polkahub"
import {
getAccountGenericAddress,
getAccountPublicKey,
selectedAccount$,
} from "@/state/polkahub"
import { polkadot_people } from "@polkadot-api/descriptors"
import {
Button,
@@ -27,6 +31,7 @@ import {
state,
SUSPENSE,
useStateObservable,
withDefault,
} from "@react-rxjs/core"
import {
createSignal,
@@ -34,7 +39,7 @@ import {
switchMapSuspended,
} from "@react-rxjs/utils"
import { ChevronLeft, Send, Settings, WalletCards } from "lucide-react"
import { AccountId, TxOptions } from "polkadot-api"
import { TxOptions } from "polkadot-api"
import {
ModalContext,
PjsWalletButtons,
@@ -45,7 +50,7 @@ import { FC, forwardRef, ReactNode, useState } from "react"
import {
catchError,
combineLatest,
defer,
distinctUntilChanged,
map,
of,
scan,
@@ -73,19 +78,17 @@ const customExtensionsCount$ = state(
const [nonceChanged$, setNonce] = createSignal<string>()
const [nonceBlurred$, blurNonce] = createSignal()
const chainNonce$ = unsafeApi$.pipe(
const chainNonce$ = unsafeApi$.pipeState(
switchMapSuspended((api) =>
selectedAccount$.pipe(
switchMapSuspended((account) =>
account?.signer
? api.apis.AccountNonceApi.account_nonce(
AccountId(42).dec(account.signer.publicKey),
{
at: "best",
},
switchMapSuspended((account) => {
const address = account && getAccountGenericAddress(account)
return address
? timer(0, 60_000).pipe(
switchMap(() => api.apis.AccountNonceApi.account_nonce(address)),
)
: [],
),
: [null]
}),
liftSuspense(),
catchError((ex) => {
console.error(ex)
@@ -95,45 +98,20 @@ const chainNonce$ = unsafeApi$.pipe(
),
liftSuspense(),
map((v) => (v === SUSPENSE ? null : (v as number))),
withDefault(null),
)
const isIntegerStr = (str: string) => /^\d+$/.test(str)
const nonce$ = state(
defer(() =>
mergeWithKey({
chainNonce$,
nonceChanged$,
nonceBlurred$,
}).pipe(
scan(
(
acc: {
chain: number | null
inputValue: string
},
v,
) => {
switch (v.type) {
case "chainNonce$":
if (v.payload != null)
return { chain: v.payload, inputValue: v.payload.toString() }
break
case "nonceBlurred$":
if (!isIntegerStr(acc.inputValue)) {
return {
chain: acc.chain,
inputValue: acc.chain?.toString() ?? "",
}
}
break
case "nonceChanged$":
return { chain: acc.chain, inputValue: v.payload }
}
return acc
},
{ chain: null, inputValue: "" },
),
map((v) => v.inputValue),
mergeWithKey({
nonceChanged$,
nonceBlurred$,
}).pipe(
scan(
(acc, v) =>
v.type === "nonceChanged$" ? v.payload : isIntegerStr(acc) ? acc : "",
"",
),
distinctUntilChanged(),
),
"",
)
@@ -178,17 +156,20 @@ const txOptions$ = state(
{} satisfies TxOptions<any, any>,
)
const paymentInfo$ = state(
export const paymentInfo$ = state(
combineLatest([transaction$, selectedAccount$, txOptions$]).pipe(
switchMapSuspended(([tx, account, txOptions]) => {
if (!tx || !account?.signer) return [null]
if (!tx || !account) return [null]
// Adding a small delay for debouncing quick input changes
return timer(200).pipe(
switchMap(() =>
tx.getPaymentInfo(account.signer!.publicKey, txOptions),
tx.getPaymentInfo(getAccountPublicKey(account), txOptions),
),
catchError(() => of(null)),
catchError((ex) => {
console.error(ex)
return of(null)
}),
)
}),
liftSuspense(),
@@ -196,16 +177,13 @@ const paymentInfo$ = state(
),
null,
)
const accountBalance$ = state(
combineLatest([selectedAccount$, client$]).pipe(
switchMapSuspended(([account, client]) =>
account?.signer
account
? client
.getTypedApi(polkadot_people)
.query.System.Account.getValue(
AccountId().dec(account.signer.publicKey),
)
.query.System.Account.getValue(getAccountGenericAddress(account))
: [],
),
liftSuspense(),
@@ -226,6 +204,7 @@ const accountBalance$ = state(
export const SubmitExtrinsic = forwardRef<HTMLElement>((_, ref) => {
const [account] = useSelectedAccount()
const chainNonce = useStateObservable(chainNonce$)
const nonce = useStateObservable(nonce$)
const mortality = useStateObservable(mortality$)
const tip = useStateObservable(tip$)
@@ -284,6 +263,7 @@ export const SubmitExtrinsic = forwardRef<HTMLElement>((_, ref) => {
type="number"
min={0}
value={nonce}
placeholder={chainNonce?.toString()}
onChange={(evt) => setNonce(evt.target.value)}
onBlur={blurNonce}
className="tabular-nums"

View File

@@ -1,6 +1,6 @@
import { polkadot_people } from "@polkadot-api/descriptors"
import { liftSuspense, state, SUSPENSE, withDefault } from "@react-rxjs/core"
import { AccountId, HexString, SS58String } from "polkadot-api"
import { AccountId, Binary, HexString, SS58String } from "polkadot-api"
import {
Account,
createLedgerProvider,
@@ -214,3 +214,20 @@ export const toggleExtension = async (id: string) => {
: [...extensions, id],
)
}
export const getAccountPublicKey = (account: Account) =>
account.signer
? account.signer.publicKey
: account.address.startsWith("0x")
? Binary.fromHex(account.address)
: AccountId().enc(account.address)
// Important, the SS58 format is not guaranteed. Only to be used for internal queries
export const getAccountGenericAddress = (account: Account) =>
account.address.startsWith("0x")
? account.signer
? Binary.toHex(account.signer.publicKey)
: account.address
: account.signer
? AccountId().dec(account.signer.publicKey)
: account.address