feat: make read-only accounts fake signers while using chopsticks
This commit is contained in:
@@ -118,5 +118,5 @@ export const createLocalStorageState = <T>(
|
||||
},
|
||||
)
|
||||
|
||||
return [state$(), setValue] as const
|
||||
return [state$, setValue] as const
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { AccountIdDisplay } from "@/components/AccountIdDisplay"
|
||||
import { AccountIdInput } from "@/components/AccountIdInput"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"
|
||||
import { readOnlyAddresses$, setAddresses } from "@/state/accounts.state"
|
||||
import { readOnlyAccounts$, setAddresses } from "@/state/accounts.state"
|
||||
import { useStateObservable } from "@react-rxjs/core"
|
||||
import { Eye, Trash2 } from "lucide-react"
|
||||
import { SS58String } from "polkadot-api"
|
||||
@@ -26,7 +26,7 @@ export const AddressProvider = () => (
|
||||
|
||||
const ManageAddresses = () => {
|
||||
const [addressInput, setAddressInput] = useState<SS58String | null>(null)
|
||||
const readOnlyAddresses = useStateObservable(readOnlyAddresses$)
|
||||
const accounts = useStateObservable(readOnlyAccounts$)
|
||||
|
||||
return (
|
||||
<div className="space-y-4 overflow-hidden">
|
||||
@@ -36,7 +36,9 @@ const ManageAddresses = () => {
|
||||
if (!addressInput) return
|
||||
|
||||
setAddresses([
|
||||
...readOnlyAddresses.filter((v) => v !== addressInput),
|
||||
...accounts
|
||||
.map((v) => v.accountId)
|
||||
.filter((v) => v !== addressInput),
|
||||
addressInput,
|
||||
])
|
||||
setAddressInput(null)
|
||||
@@ -56,23 +58,27 @@ const ManageAddresses = () => {
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
{readOnlyAddresses.length ? (
|
||||
{accounts.length ? (
|
||||
<div>
|
||||
<h3 className="font-medium text-muted-foreground">Added addresses</h3>
|
||||
<ul className="space-y-3">
|
||||
{readOnlyAddresses.map((addr) => (
|
||||
<li key={addr} className="flex gap-2 items-center">
|
||||
{accounts.map((account) => (
|
||||
<li key={account.accountId} className="flex gap-2 items-center">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="text-destructive border-destructive"
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setAddresses(readOnlyAddresses.filter((v) => addr !== v))
|
||||
setAddresses(
|
||||
accounts
|
||||
.map((v) => v.accountId)
|
||||
.filter((v) => account.accountId !== v),
|
||||
)
|
||||
}
|
||||
>
|
||||
<Trash2 />
|
||||
</Button>
|
||||
<AccountIdDisplay value={addr} />
|
||||
<AccountIdDisplay value={account.accountId} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -70,7 +70,7 @@ const groupedAccounts$ = allAccounts$.pipeState(
|
||||
map((v) =>
|
||||
groupBy(
|
||||
// Accept only signer accounts for submitting transactions
|
||||
v.filter((v) => "signer" in v),
|
||||
v.filter((v) => v.signer != null),
|
||||
(acc) => (acc.type === "extension" ? acc.extensionId : acc.type),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -22,11 +22,13 @@ const SignAndSubmit: FC<{ callData: string; onClose: () => void }> = ({
|
||||
return (
|
||||
<ActionButton
|
||||
onClick={async () => {
|
||||
if (!account?.signer) return
|
||||
|
||||
setIsSigning(true)
|
||||
try {
|
||||
const unsafeApi = await firstValueFrom(unsafeApi$)
|
||||
const tx = await unsafeApi.txFromCallData(Binary.fromHex(callData))
|
||||
const signedExtrinsic = await tx.sign(account!.signer)
|
||||
const signedExtrinsic = await tx.sign(account.signer)
|
||||
trackSignedTx(signedExtrinsic)
|
||||
onClose()
|
||||
} catch (ex) {
|
||||
|
||||
@@ -3,7 +3,9 @@ import { state } from "@react-rxjs/core"
|
||||
import type { HexString, PolkadotSigner, SS58String } from "polkadot-api"
|
||||
import type { InjectedPolkadotAccount } from "polkadot-api/pjs-signer"
|
||||
import { combineLatest, defer, from, map, switchMap } from "rxjs"
|
||||
import { accountsByExtension$ } from "./extension-accounts.state"
|
||||
import { accountsByExtension$, getPublicKey } from "./extension-accounts.state"
|
||||
import { getPolkadotSigner } from "polkadot-api/signer"
|
||||
import { canSetStorage$ } from "./chains/chain.state"
|
||||
|
||||
export type AccountSource = "extension" | "walletconnect" | "readonly"
|
||||
export const accountSourceTypeToName: Record<AccountSource, string> = {
|
||||
@@ -73,20 +75,35 @@ export const walletConnectAccounts$ = state(
|
||||
|
||||
export interface ReadOnlyAccount extends BaseAccount {
|
||||
type: "readonly"
|
||||
// Chopsticks can fake a signer
|
||||
signer?: PolkadotSigner
|
||||
}
|
||||
export const [readOnlyAddresses$, setAddresses] = createLocalStorageState(
|
||||
const [readOnlyAddresses$, setAddresses] = createLocalStorageState(
|
||||
"read-only-addr",
|
||||
[] as string[],
|
||||
)
|
||||
export const readOnlyAccounts$ = readOnlyAddresses$.pipe(
|
||||
map((v) =>
|
||||
v.map(
|
||||
(address): ReadOnlyAccount => ({
|
||||
type: "readonly",
|
||||
accountId: address,
|
||||
}),
|
||||
export { setAddresses }
|
||||
export const readOnlyAccounts$ = state(
|
||||
combineLatest([defer(readOnlyAddresses$), canSetStorage$]).pipe(
|
||||
map(([v, isChopsticks]) =>
|
||||
v.map(
|
||||
(address): ReadOnlyAccount => ({
|
||||
type: "readonly",
|
||||
accountId: address,
|
||||
signer: isChopsticks
|
||||
? getPolkadotSigner(getPublicKey(address)!, "Sr25519", () => {
|
||||
// From https://wiki.acala.network/build/sdks/homa
|
||||
const signature = new Uint8Array(64)
|
||||
signature.fill(0xcd)
|
||||
signature.set([0xde, 0xad, 0xbe, 0xef])
|
||||
return signature
|
||||
})
|
||||
: undefined,
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
[],
|
||||
)
|
||||
|
||||
export type Account = ExtensionAccount | WalletConnectAccount | ReadOnlyAccount
|
||||
|
||||
Reference in New Issue
Block a user