From 9597670f80c00df5ef0d0996429194c1fe59a7cc Mon Sep 17 00:00:00 2001 From: mtyszczak <mateusz.tyszczak@gmail.com> Date: Thu, 13 Mar 2025 12:13:22 +0100 Subject: [PATCH] Add account authority update operation --- .../wallets/metamask/MetamaskConnect.vue | 19 ++-- src/components/sidebar/AppSidebar.vue | 7 +- .../utilcards/ConfirmAccountUpdateCard.vue | 93 +++++++++++++++++++ .../utilcards/ConfirmCreateAccountCard.vue | 6 +- .../utilcards/SignTransactionCard.vue | 9 +- src/pages/account/update.vue | 9 ++ src/utils/router.ts | 4 +- 7 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 src/components/utilcards/ConfirmAccountUpdateCard.vue create mode 100644 src/pages/account/update.vue diff --git a/src/components/onboarding/wallets/metamask/MetamaskConnect.vue b/src/components/onboarding/wallets/metamask/MetamaskConnect.vue index 3aa6143..b5c6d37 100644 --- a/src/components/onboarding/wallets/metamask/MetamaskConnect.vue +++ b/src/components/onboarding/wallets/metamask/MetamaskConnect.vue @@ -146,7 +146,7 @@ const generateAccountUpdateTransaction = async(): Promise<string> => { tx.pushOperation(op); return tx.toApi(); }; -const getAccountCreateSigningLink = async () => { +const getAccountCreateSigningLink = (): string => { const accountName = createAccountNameOperation.value!.startsWith('@') ? createAccountNameOperation.value!.slice(1) : createAccountNameOperation.value!; return `${window.location.protocol}//${window.location.host}/account/create?acc=${accountName}&posting=${ metamaskPublicKeys.value!.find(node => node.role === "posting")!.publicKey @@ -158,9 +158,14 @@ const getAccountCreateSigningLink = async () => { metamaskPublicKeys.value!.find(node => node.role === "memo")!.publicKey }`; }; -const getSigningLink = async () => { - const tx = await generateAccountUpdateTransaction(); - return `${window.location.protocol}//${window.location.host}/sign/transaction?data=${btoa(tx)}`; +const getAuthorityUpdateSigningLink = (): string => { + const accountName = updateAccountNameOperation.value!.startsWith('@') ? updateAccountNameOperation.value!.slice(1) : updateAccountNameOperation.value!; + const url = new URL(`${window.location.protocol}//${window.location.host}/account/update?acc=${accountName}`); + for(const key in updateAuthType) + if (updateAuthType[key as TRole]) + url.searchParams.set(key, metamaskPublicKeys.value!.find(node => node.role === key)!.publicKey); + + return url.toString(); }; const updateAccountName = (value: string | any) => { @@ -231,12 +236,12 @@ const updateAccountName = (value: string | any) => { </div> </div> <div class="flex items-center flex-col"> - <Button :disabled="isLoading" @click="getSigningLink().then(copyContent)" variant="outline" size="lg" class="mt-4 px-8 py-4 border-[#FF5C16] border-[1px]"> + <Button :disabled="isLoading" @click="copyContent(getAuthorityUpdateSigningLink())" variant="outline" size="lg" class="mt-4 px-8 py-4 border-[#FF5C16] border-[1px]"> <span class="text-md font-bold">Copy signing link</span> </Button> <Separator label="Or" class="mt-8" /> <div class="flex justify-center mt-4"> - <Button :disabled="isLoading" @click="generateAccountUpdateTransaction().then(copyContent)" variant="outline" size="lg" class="px-8 opacity-[0.9] py-4 border-[#FF5C16] border-[1px]"> + <Button :disabled="isLoading" @click="generateAccountUpdateTransaction()" variant="outline" size="lg" class="px-8 opacity-[0.9] py-4 border-[#FF5C16] border-[1px]"> <span class="text-md font-bold">Copy entire transaction</span> </Button> </div> @@ -256,7 +261,7 @@ const updateAccountName = (value: string | any) => { </div> </div> <div class="flex items-center flex-col"> - <Button :disabled="isLoading" @click="getAccountCreateSigningLink().then(copyContent)" variant="outline" size="lg" class="mt-4 px-8 py-4 border-[#FF5C16] border-[1px]"> + <Button :disabled="isLoading" @click="copyContent(getAccountCreateSigningLink())" variant="outline" size="lg" class="mt-4 px-8 py-4 border-[#FF5C16] border-[1px]"> <span class="text-md font-bold">Copy signing link</span> </Button> </div> diff --git a/src/components/sidebar/AppSidebar.vue b/src/components/sidebar/AppSidebar.vue index 05fa72a..377339a 100644 --- a/src/components/sidebar/AppSidebar.vue +++ b/src/components/sidebar/AppSidebar.vue @@ -1,5 +1,5 @@ <script setup lang="ts"> -import { mdiHomeOutline, mdiMessageLockOutline, mdiFileSign, mdiAccountPlusOutline } from "@mdi/js" +import { mdiHomeOutline, mdiMessageLockOutline, mdiFileSign, mdiAccountPlusOutline, mdiAccountArrowUpOutline } from "@mdi/js" import { Sidebar, SidebarContent, SidebarHeader, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar" import { useSidebar } from "@/components/ui/sidebar"; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' @@ -37,6 +37,11 @@ const items = [ title: "Process Account Creation", url: "/account/create", icon: mdiAccountPlusOutline, + }, + { + title: "Process Authority Update", + url: "/account/update", + icon: mdiAccountArrowUpOutline, } ]; </script> diff --git a/src/components/utilcards/ConfirmAccountUpdateCard.vue b/src/components/utilcards/ConfirmAccountUpdateCard.vue new file mode 100644 index 0000000..44755b9 --- /dev/null +++ b/src/components/utilcards/ConfirmAccountUpdateCard.vue @@ -0,0 +1,93 @@ +<script setup lang="ts"> +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { mdiAccountArrowUpOutline } from '@mdi/js'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { useSettingsStore } from '@/stores/settings.store'; +import { onMounted, ref } from 'vue'; +import { useRouter } from 'vue-router'; +import { getWax } from '@/stores/wax.store'; +import { useWalletStore } from '@/stores/wallet.store'; +import { AccountAuthorityUpdateOperation } from '@hiveio/wax'; + +const settings = useSettingsStore(); + +const creator = ref<string>(''); +const memoKey = ref<string>(''); +const postingKey = ref<string>(''); +const activeKey = ref<string>(''); +const ownerKey = ref<string>(''); + +const router = useRouter(); +const wallet = useWalletStore(); + +onMounted(() => { + creator.value = router.currentRoute.value.query.acc as string ?? (settings.account ? `@${settings.account}` : ''); + + memoKey.value = router.currentRoute.value.query.memo as string ?? null; + postingKey.value = router.currentRoute.value.query.posting as string ?? null; + activeKey.value = router.currentRoute.value.query.active as string ?? null; + ownerKey.value = router.currentRoute.value.query.owner as string ?? null; +}); + +const updateAuthority = async() => { + if (!memoKey.value && !postingKey.value && !activeKey.value && !ownerKey.value) { + alert('Nothing to update'); + return; + } + + const wax = await getWax(); + const tx = await wax.createTransaction(); + const op = await AccountAuthorityUpdateOperation.createFor(wax, creator.value.startsWith('@') ? creator.value.slice(1) : creator.value); + if (memoKey.value) + op.role("memo").set(memoKey.value); + if (postingKey.value) + op.role("posting").add(postingKey.value); + if (activeKey.value) + op.role("active").add(activeKey.value); + if (ownerKey.value) + op.role("owner").add(ownerKey.value); + tx.pushOperation(op); + const signature = await wallet.wallet!.signTransaction(tx, ownerKey.value ? "owner" : "active"); + tx.sign(signature); + await wax.broadcast(tx); +} +</script> + +<template> + <Card class="w-full max-w-[600px] bg-black/40 backdrop-blur-sm"> + <CardHeader> + <CardTitle class="inline-flex items-center justify-between"> + <span>Process Authority Update</span> + <svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill: hsla(var(--foreground) / 80%)" :d="mdiAccountArrowUpOutline"/></svg> + </CardTitle> + <CardDescription class="mr-8">Use this module to process account authority update request sent by Metamask Snap</CardDescription> + </CardHeader> + <CardContent> + <div class="my-4 space-y-2"> + <div class="grid w-full max-w-sm items-center"> + <Label for="updateAuthority_creator">Account To Update</Label> + <Input id="updateAuthority_creator" v-model="creator" class="my-2" /> + </div> + <div class="grid w-full max-w-sm items-center"> + <Label for="updateAuthority_memoKey">New Memo Key</Label> + <Input id="updateAuthority_memoKey" placeholder="Nothing to update" v-model="memoKey" class="my-2" /> + </div> + <div class="grid w-full max-w-sm items-center"> + <Label for="updateAuthority_postingKey">Add Posting Key</Label> + <Input id="updateAuthority_postingKey" placeholder="Nothing to add" v-model="postingKey" class="my-2" /> + </div> + <div class="grid w-full max-w-sm items-center"> + <Label for="updateAuthority_activeKey">Add Active Key</Label> + <Input id="updateAuthority_activeKey" placeholder="Nothing to add" v-model="activeKey" class="my-2" /> + </div> + <div class="grid w-full max-w-sm items-center"> + <Label for="updateAuthority_ownerKey">Add Owner Key</Label> + <Input id="updateAuthority_ownerKey" placeholder="Nothing to add" v-model="ownerKey" class="my-2" /> + </div> + <Button class="my-2" @click="updateAuthority">Update Authority</Button> + </div> + </CardContent> + </Card> +</template> \ No newline at end of file diff --git a/src/components/utilcards/ConfirmCreateAccountCard.vue b/src/components/utilcards/ConfirmCreateAccountCard.vue index a245c2f..287c2f0 100644 --- a/src/components/utilcards/ConfirmCreateAccountCard.vue +++ b/src/components/utilcards/ConfirmCreateAccountCard.vue @@ -41,7 +41,7 @@ const createAccount = async() => { tx.pushOperation({ account_create: { creator: settings.account!, - new_account_name: accountName.value, + new_account_name: accountName.value.startsWith('@') ? accountName.value.slice(1) : accountName.value, memo_key: memoKey.value, owner: { weight_threshold: 1, @@ -80,11 +80,11 @@ const createAccount = async() => { <CardContent> <div class="my-4 space-y-2"> <div class="grid w-full max-w-sm items-center"> - <Label for="createAccount_creator">Account name</Label> + <Label for="createAccount_creator">Creator Account Name</Label> <Input id="createAccount_creator" v-model="creator" class="my-2" disabled /> </div> <div class="grid w-full max-w-sm items-center"> - <Label for="createAccount_accountName">Account Name</Label> + <Label for="createAccount_accountName">New Account Name</Label> <Input id="createAccount_accountName" v-model="accountName" class="my-2" /> </div> <div class="grid w-full max-w-sm items-center"> diff --git a/src/components/utilcards/SignTransactionCard.vue b/src/components/utilcards/SignTransactionCard.vue index 91ef8d4..b5b1c20 100644 --- a/src/components/utilcards/SignTransactionCard.vue +++ b/src/components/utilcards/SignTransactionCard.vue @@ -1,12 +1,13 @@ <script setup lang="ts"> import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { mdiFileSign } from '@mdi/js'; -import { computed, ref } from 'vue'; +import { computed, onMounted, ref } from 'vue'; import { Textarea } from '@/components/ui/textarea'; import { Button } from '@/components/ui/button'; import { useWalletStore } from '@/stores/wallet.store'; import { getWax } from '@/stores/wax.store'; import type { TRole } from '@hiveio/wax/vite'; +import { useRouter } from 'vue-router'; const walletStore = useWalletStore(); @@ -16,6 +17,8 @@ const wallet = computed(() => walletStore.wallet); const inputData = ref(''); const outputData = ref(''); +const router = useRouter(); + const sign = async () => { const wax = await getWax(); @@ -32,6 +35,10 @@ const sign = async () => { outputData.value = await wallet.value!.signTransaction(tx, authorityLevel); }; + +onMounted(() => { + inputData.value = decodeURIComponent(atob(router.currentRoute.value.query.data as string ?? '')); +}); </script> <template> diff --git a/src/pages/account/update.vue b/src/pages/account/update.vue new file mode 100644 index 0000000..d59a7cf --- /dev/null +++ b/src/pages/account/update.vue @@ -0,0 +1,9 @@ +<script setup lang="ts"> +import ConfirmAccountUpdateCard from '@/components/utilcards/ConfirmAccountUpdateCard.vue'; +</script> + +<template> + <div class="flex py-4 px-8"> + <ConfirmAccountUpdateCard /> + </div> +</template> diff --git a/src/utils/router.ts b/src/utils/router.ts index 1d5a8fa..86dac72 100644 --- a/src/utils/router.ts +++ b/src/utils/router.ts @@ -2,10 +2,12 @@ import Index from "@/pages/index.vue"; import SignTransaction from "@/pages/sign/transaction.vue"; import SignMessage from "@/pages/sign/message.vue"; import AccountCreate from "@/pages/account/create.vue"; +import AccountUpdate from "@/pages/account/update.vue"; export const routes = [ { path: '/', component: Index }, { path: '/sign/transaction', component: SignTransaction }, { path: '/sign/message', component: SignMessage }, - { path: '/account/create', component: AccountCreate } + { path: '/account/create', component: AccountCreate }, + { path: '/account/update', component: AccountUpdate } ]; -- GitLab