From 0e74c396f46618285c753066452570daf44883c7 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 16 Apr 2025 19:03:35 +0300 Subject: [PATCH] added qrs , modified blocks, --- src/App.tsx | 56 +++- src/atoms/global.ts | 12 + src/background.ts | 144 +++++++++ src/components/Apps/AppsLibrary.tsx | 4 +- src/components/Apps/AppsLibraryDesktop.tsx | 4 +- .../Apps/useQortalMessageListener.tsx | 28 +- src/components/Chat/useBlockUsers.tsx | 133 ++++---- src/components/Group/BlockedUsersModal.tsx | 289 +++++++++++++---- src/components/Group/Group.tsx | 19 +- src/components/WrapperUserAction.tsx | 10 +- src/qortalRequests.ts | 57 +++- src/qortalRequests/get.ts | 292 +++++++++++++++++- src/transactions/BuyNameTransacion.ts | 45 +++ src/transactions/CancelSellNameTransacion.ts | 33 ++ src/transactions/SellNameTransacion.ts | 40 +++ src/transactions/UpdateGroupTransaction.ts | 62 ++++ src/transactions/transactions.ts | 8 + 17 files changed, 1090 insertions(+), 146 deletions(-) create mode 100644 src/transactions/BuyNameTransacion.ts create mode 100644 src/transactions/CancelSellNameTransacion.ts create mode 100644 src/transactions/SellNameTransacion.ts create mode 100644 src/transactions/UpdateGroupTransaction.ts diff --git a/src/App.tsx b/src/App.tsx index 140cc77..5b33b5d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,6 +21,7 @@ import { DialogContent, DialogContentText, DialogTitle, + FormControlLabel, Input, InputLabel, Popover, @@ -139,6 +140,8 @@ import { useBlockedAddresses } from "./components/Chat/useBlockUsers"; import { QortPayment } from "./components/QortPayment"; import { GeneralNotifications } from "./components/GeneralNotifications"; import { PdfViewer } from "./common/PdfViewer"; +import PriorityHighIcon from '@mui/icons-material/PriorityHigh'; + type extStates = | "not-authenticated" @@ -354,6 +357,8 @@ function App() { url: "http://127.0.0.1:12391", }); const [useLocalNode, setUseLocalNode] = useState(false); + const [confirmRequestRead, setConfirmRequestRead] = useState(false); + const {downloadResource} = useFetchResources() const [showSeed, setShowSeed] = useState(false) const [creationStep, setCreationStep] = useState(1) @@ -716,6 +721,8 @@ function App() { if(message?.payload?.checkbox1){ qortalRequestCheckbox1Ref.current = message?.payload?.checkbox1?.value || false } + setConfirmRequestRead(false) + await showQortalRequestExtension(message?.payload); if (qortalRequestCheckbox1Ref.current) { @@ -2157,11 +2164,13 @@ function App() { justifyContent: "flex-start", paddingLeft: "22px", boxSizing: "border-box", + maxWidth: '700px' }} > { setRawWallet(null); @@ -2689,11 +2700,13 @@ function App() { justifyContent: "flex-start", paddingLeft: "22px", boxSizing: "border-box", + maxWidth: '700px' }} > { setRawWallet(null); @@ -2793,11 +2806,13 @@ function App() { justifyContent: "flex-start", paddingLeft: "22px", boxSizing: "border-box", + maxWidth: '700px' }} > { if(creationStep === 2){ @@ -3533,6 +3550,35 @@ function App() { )} + {messageQortalRequestExtension?.confirmCheckbox && ( + setConfirmRequestRead(e.target.checked)} + checked={confirmRequestRead} + edge="start" + tabIndex={-1} + disableRipple + sx={{ + "&.Mui-checked": { + color: "white", + }, + "& .MuiSvgIcon-root": { + color: "white", + }, + }} + /> + } + label={ + + + I have read this request + + + + } + /> + )} { + if(messageQortalRequestExtension?.confirmCheckbox && !confirmRequestRead) return + onOkQortalRequestExtension("accepted") }} - onClick={() => onOkQortalRequestExtension("accepted")} > accept diff --git a/src/atoms/global.ts b/src/atoms/global.ts index c34a9fa..9502661 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -37,6 +37,14 @@ export const sortablePinnedAppsAtom = atom({ { name: 'Q-Wallets', service: 'APP' + }, + { + name: 'Q-Search', + service: 'APP' + }, + { + name: 'Q-Nodecontrol', + service: 'APP' } ], }); @@ -163,4 +171,8 @@ export const groupsPropertiesAtom = atom({ export const lastPaymentSeenTimestampAtom = atom({ key: 'lastPaymentSeenTimestampAtom', default: null, +}); +export const isOpenBlockedModalAtom = atom({ + key: 'isOpenBlockedModalAtom', + default: false, }); \ No newline at end of file diff --git a/src/background.ts b/src/background.ts index 543960a..c028ff2 100644 --- a/src/background.ts +++ b/src/background.ts @@ -2673,6 +2673,150 @@ export async function createGroup({ if (!res?.signature) throw new Error(res?.message || "Transaction was not able to be processed"); return res; } + +export async function sellName({ + name, + sellPrice +}) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + if (!address) throw new Error("Cannot find user"); + const lastReference = await getLastRef(); + const feeres = await getFee("SELL_NAME"); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const tx = await createTransaction(5, keyPair, { + fee: feeres.fee, + name, + sellPrice: sellPrice, + lastReference: lastReference, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error(res?.message || "Transaction was not able to be processed"); + return res; +} + +export async function cancelSellName({ + name +}) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + if (!address) throw new Error("Cannot find user"); + const lastReference = await getLastRef(); + const feeres = await getFee("SELL_NAME"); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const tx = await createTransaction(6, keyPair, { + fee: feeres.fee, + name, + lastReference: lastReference, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error(res?.message || "Transaction was not able to be processed"); + return res; +} + +export async function buyName({ + name, + sellerAddress, + sellPrice +}) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + if (!address) throw new Error("Cannot find user"); + const lastReference = await getLastRef(); + const feeres = await getFee("BUY_NAME"); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const tx = await createTransaction(7, keyPair, { + fee: feeres.fee, + name, + sellPrice, + recipient: sellerAddress, + lastReference: lastReference, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error(res?.message || "Transaction was not able to be processed"); + return res; +} + +export async function updateGroup({ + groupId, + newOwner, + newIsOpen, + newDescription, + newApprovalThreshold, + newMinimumBlockDelay, + newMaximumBlockDelay +}) { + const wallet = await getSaveWallet(); + const address = wallet.address0; + if (!address) throw new Error("Cannot find user"); + const lastReference = await getLastRef(); + const feeres = await getFee("UPDATE_GROUP"); + const resKeyPair = await getKeyPair(); + const parsedData = resKeyPair; + const uint8PrivateKey = Base58.decode(parsedData.privateKey); + const uint8PublicKey = Base58.decode(parsedData.publicKey); + const keyPair = { + privateKey: uint8PrivateKey, + publicKey: uint8PublicKey, + }; + + const tx = await createTransaction(23, keyPair, { + fee: feeres.fee, + _groupId: groupId, + newOwner, + newIsOpen, + newDescription, + newApprovalThreshold, + newMinimumBlockDelay, + newMaximumBlockDelay, + lastReference: lastReference, + }); + + const signedBytes = Base58.encode(tx.signedBytes); + + const res = await processTransactionVersion2(signedBytes); + if (!res?.signature) + throw new Error(res?.message || "Transaction was not able to be processed"); + return res; +} + + export async function inviteToGroup({ groupId, qortalAddress, inviteTime }) { const address = await getNameOrAddress(qortalAddress); if (!address) throw new Error("Cannot find user"); diff --git a/src/components/Apps/AppsLibrary.tsx b/src/components/Apps/AppsLibrary.tsx index 09f9eff..29c21df 100644 --- a/src/components/Apps/AppsLibrary.tsx +++ b/src/components/Apps/AppsLibrary.tsx @@ -43,7 +43,9 @@ const officialAppList = [ "q-shop", "q-trade", "q-support", - "q-wallets" + "q-wallets", + "q-search", + "q-nodecontrol" ]; const ScrollerStyled = styled('div')({ diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index 3c0d62b..cb36225 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -60,7 +60,9 @@ const officialAppList = [ "q-support", "q-mintership", "q-manager", - "q-wallets" + "q-wallets", + "q-search", + "q-nodecontrol" ]; const ScrollerStyled = styled("div")({ diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index 26a77e0..d7ad040 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -247,7 +247,10 @@ const UIQortalRequests = [ 'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER', 'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER', 'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_PUBLIC_NODE', 'ADMIN_ACTION', 'SIGN_TRANSACTION', 'DECRYPT_QORTAL_GROUP_DATA', 'DELETE_HOSTED_DATA', 'GET_HOSTED_DATA', 'DECRYPT_DATA_WITH_SHARING_KEY', 'SHOW_ACTIONS', 'REGISTER_NAME', 'UPDATE_NAME', 'LEAVE_GROUP', 'INVITE_TO_GROUP', 'KICK_FROM_GROUP', 'BAN_FROM_GROUP', 'CANCEL_GROUP_BAN', 'ADD_GROUP_ADMIN','REMOVE_GROUP_ADMIN','DECRYPT_AESGCM', 'CANCEL_GROUP_INVITE', 'CREATE_GROUP', 'GET_USER_WALLET_TRANSACTIONS', 'GET_NODE_INFO', - 'GET_NODE_STATUS', 'GET_ARRR_SYNC_STATUS', 'SHOW_PDF_READER' + 'GET_NODE_STATUS', 'GET_ARRR_SYNC_STATUS', 'UPDATE_GROUP', + 'SELL_NAME', +'CANCEL_SELL_NAME', +'BUY_NAME' ]; @@ -614,7 +617,7 @@ isDOMContentLoaded: false ); } else if(event?.data?.action === 'OPEN_NEW_TAB'){ try { - await openNewTab(event?.data?.payload) + await openNewTab(event?.data) event.ports[0].postMessage({ result: true, error: null, @@ -639,6 +642,27 @@ isDOMContentLoaded: false error: error?.message, }); } + } else if(event?.data?.action === 'SHOW_PDF_READER'){ + try { + if(!event?.data?.blob){ + throw new Error('Missing blob') + } + if(event?.data?.blob?.type !== "application/pdf") throw new Error('blob type must be application/pdf') + + + executeEvent("openPdf", { blob: event?.data?.blob}); + + + event.ports[0].postMessage({ + result: true, + error: null, + }); + } catch (error) { + event.ports[0].postMessage({ + result: null, + error: error?.message, + }); + } } }; diff --git a/src/components/Chat/useBlockUsers.tsx b/src/components/Chat/useBlockUsers.tsx index aaef617..b787735 100644 --- a/src/components/Chat/useBlockUsers.tsx +++ b/src/components/Chat/useBlockUsers.tsx @@ -1,6 +1,5 @@ import React, { useCallback, useEffect, useRef } from "react"; -import { getBaseApiReact } from "../../App"; -import { truncate } from "lodash"; + @@ -19,7 +18,7 @@ export const useBlockedAddresses = () => { const isUserBlocked = useCallback((address, name)=> { try { if(!address) return false - if(userBlockedRef.current[address] || userNamesBlockedRef.current[name]) return true + if(userBlockedRef.current[address]) return true return false @@ -88,47 +87,47 @@ export const useBlockedAddresses = () => { } fetchBlockedList() }, []) - const removeBlockFromList = useCallback(async (address, name)=> { - await new Promise((res, rej) => { - chrome?.runtime?.sendMessage( - { - action: "listActions", - payload: { - type: 'remove', - items: name ? [name] : [address], - listName: name ? 'blockedNames' : 'blockedAddresses' + if(name){ + await new Promise((res, rej) => { + + chrome?.runtime?.sendMessage( + { + action: "listActions", + payload: { + type: 'remove', + items: [name] , + listName: 'blockedNames' + }, }, - }, - (response) => { - if (response.error) { - rej(response?.message); - return; - } else { - if(!name){ - const copyObject = {...userBlockedRef.current} - delete copyObject[address] - userBlockedRef.current = copyObject + (response) => { + if (response.error) { + rej(response?.message); + return; } else { const copyObject = {...userNamesBlockedRef.current} delete copyObject[name] userNamesBlockedRef.current = copyObject - } + + res(response); + } } - } - ); + ); }) - if(name && userBlockedRef.current[address]){ + + } + + if(address){ await new Promise((res, rej) => { chrome?.runtime?.sendMessage( { action: "listActions", payload: { type: 'remove', - items: !name ? [name] : [address], - listName: !name ? 'blockedNames' : 'blockedAddresses' + items: [address], + listName: 'blockedAddresses' }, }, (response) => { @@ -139,47 +138,77 @@ export const useBlockedAddresses = () => { const copyObject = {...userBlockedRef.current} delete copyObject[address] userBlockedRef.current = copyObject - res(response); + + + res(response); } } ); - }) + }) } + }, []) const addToBlockList = useCallback(async (address, name)=> { - await new Promise((res, rej) => { - chrome?.runtime?.sendMessage( - { - action: "listActions", - payload: { - type: 'add', - items: name ? [name] : [address], - listName: name ? 'blockedNames' : 'blockedAddresses' + if(name){ + await new Promise((res, rej) => { + + + chrome?.runtime?.sendMessage( + { + action: "listActions", + payload: { + type: 'add', + items: [name], + listName: 'blockedNames' + }, }, - }, - (response) => { - if (response.error) { - rej(response?.message); - return; - } else { - if(name){ - + (response) => { + if (response.error) { + rej(response?.message); + return; + } else { const copyObject = {...userNamesBlockedRef.current} copyObject[name] = true userNamesBlockedRef.current = copyObject - }else { + + + res(response); + } + } + ); + }) + } + if(address){ + await new Promise((res, rej) => { + chrome?.runtime?.sendMessage( + { + action: "listActions", + payload: { + type: 'add', + items: [address], + listName: 'blockedAddresses' + }, + }, + (response) => { + if (response.error) { + rej(response?.message); + return; + } else { const copyObject = {...userBlockedRef.current} copyObject[address] = true userBlockedRef.current = copyObject - - } + res(response); + } } - } - ) - }) + ); + }) + + + } + }, []) return { diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index 84fa3fa..f82d632 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -10,15 +10,23 @@ import { Typography, } from "@mui/material"; import React, { useContext, useEffect, useState } from "react"; -import { MyContext } from "../../App"; +import { getBaseApiReact, MyContext } from "../../App"; import { Spacer } from "../../common/Spacer"; -import { executeEvent } from "../../utils/events"; - -export const BlockedUsersModal = ({ close }) => { +import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; +import { validateAddress } from "../../utils/validateAddress"; +import { getNameInfo, requestQueueMemberNames } from "./Group"; +import { useModal } from "../../common/useModal"; +import { useRecoilState } from "recoil"; +import { isOpenBlockedModalAtom } from "../../atoms/global"; +import InfoIcon from '@mui/icons-material/Info'; +export const BlockedUsersModal = () => { + const [isOpenBlockedModal, setIsOpenBlockedModal] = useRecoilState(isOpenBlockedModalAtom) const [hasChanged, setHasChanged] = useState(false); const [value, setValue] = useState(""); - - const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = useContext(MyContext); + const [addressesWithNames, setAddressesWithNames] = useState({}) + const { isShow, onCancel, onOk, show, message } = useModal(); + const { getAllBlockedUsers, removeBlockFromList, addToBlockList, setOpenSnackGlobal, setInfoSnackCustom } = + useContext(MyContext); const [blockedUsers, setBlockedUsers] = useState({ addresses: {}, names: {}, @@ -28,60 +36,162 @@ export const BlockedUsersModal = ({ close }) => { }; useEffect(() => { + if(!isOpenBlockedModal) return fetchBlockedUsers(); - }, []); + }, [isOpenBlockedModal]); + + const getNames = async () => { + // const validApi = await findUsableApi(); + const addresses = Object.keys(blockedUsers?.addresses) + const addressNames = {} + + + const getMemNames = addresses.map(async (address) => { + const name = await requestQueueMemberNames.enqueue(() => { + return getNameInfo(address); + }); + if (name) { + addressNames[address] = name + } + + + return true; + }); + + await Promise.all(getMemNames); + + setAddressesWithNames(addressNames) + }; + + const blockUser = async (e, user?: string) => { + try { + const valUser = user || value + if (!valUser) return; + const isAddress = validateAddress(valUser); + let userName = null; + let userAddress = null; + if (isAddress) { + userAddress = valUser; + const name = await getNameInfo(valUser); + if (name) { + userName = name; + } + } + if (!isAddress) { + const response = await fetch(`${getBaseApiReact()}/names/${valUser}`); + const data = await response.json(); + if (!data?.owner) throw new Error("Name does not exist"); + if (data?.owner) { + userAddress = data.owner; + userName = valUser; + } + } + if(!userName){ + await addToBlockList(userAddress, null); + fetchBlockedUsers(); + setHasChanged(true); + executeEvent('updateChatMessagesWithBlocks', true) + setValue('') + return + } + const responseModal = await show({ + userName, + userAddress, + }); + if (responseModal === "both") { + await addToBlockList(userAddress, userName); + } else if (responseModal === "address") { + await addToBlockList(userAddress, null); + } else if (responseModal === "name") { + await addToBlockList(null, userName); + } + fetchBlockedUsers(); + setHasChanged(true); + setValue('') + if(user){ + setIsOpenBlockedModal(false) + } + if(responseModal === 'both' || responseModal === 'address'){ + executeEvent('updateChatMessagesWithBlocks', true) + } + } catch (error) { + setOpenSnackGlobal(true); + + setInfoSnackCustom({ + type: "error", + message: error?.message || "Unable to block user", + }); + } + }; + const blockUserFromOutsideModalFunc = (e) => { + const user = e.detail?.user; + setIsOpenBlockedModal(true) + blockUser(null, user) + }; + + useEffect(() => { + subscribeToEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); + + return () => { + unsubscribeFromEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); + }; + }, []); return ( - Blocked Users - - Blocked Users + - { - setValue(e.target.value); + > + - - - + > + { + setValue(e.target.value); + }} + /> + + + {Object.entries(blockedUsers?.addresses).length > 0 && ( <> - Blocked Users for Chat ( addresses ) + Blocked addresses- blocks processing of txs + + )} - + {Object.entries(blockedUsers?.addresses || {})?.map( ([key, value]) => { return ( @@ -90,18 +200,22 @@ export const BlockedUsersModal = ({ close }) => { display: "flex", alignItems: "center", gap: "10px", - width: '100%', - justifyContent: 'space-between' + width: "100%", + justifyContent: "space-between", }} > - {key} + {addressesWithNames[key] || key} + + + + {"Decide what to block"} + + + + Blocking {message?.userName || message?.userAddress} + + + Choose "block txs" or "all" to block chat messages + + + + + + + + ); }; diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 77f8d2e..58286e2 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -95,13 +95,15 @@ import { formatEmailDate } from "./QMailMessages"; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { useRecoilState, useSetRecoilState } from "recoil"; -import { addressInfoControllerAtom, groupsPropertiesAtom, selectedGroupIdAtom } from "../../atoms/global"; +import { addressInfoControllerAtom, groupsPropertiesAtom, isOpenBlockedModalAtom, selectedGroupIdAtom } from "../../atoms/global"; import { sortArrayByTimestampAndGroupName } from "../../utils/time"; import { AdminSpace } from "../Chat/AdminSpace"; import { HubsIcon } from "../../assets/Icons/HubsIcon"; import { MessagingIcon } from "../../assets/Icons/MessagingIcon"; import { DesktopSideBar } from "../DesktopSideBar"; -import BlockIcon from '@mui/icons-material/Block'; + +import PersonOffIcon from '@mui/icons-material/PersonOff'; + import { BlockedUsersModal } from "./BlockedUsersModal"; import { WalletsAppWrapper } from "./WalletsAppWrapper"; @@ -477,6 +479,8 @@ export const Group = ({ const [groupAnnouncements, setGroupAnnouncements] = React.useState({}); const [defaultThread, setDefaultThread] = React.useState(null); const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); + const setIsOpenBlockedUserModal = useSetRecoilState(isOpenBlockedModalAtom) + const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false); const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(""); const [drawerMode, setDrawerMode] = React.useState("groups"); @@ -484,7 +488,6 @@ export const Group = ({ const [mobileViewMode, setMobileViewMode] = useState("home"); const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState(""); const isFocusedRef = useRef(true); - const [isOpenBlockedUserModal, setIsOpenBlockedUserModal] = React.useState(false); const timestampEnterDataRef = useRef({}); const selectedGroupRef = useRef(null); const selectedDirectRef = useRef(null); @@ -2255,7 +2258,7 @@ export const Group = ({ padding: '10px' }} > - )} - {isOpenBlockedUserModal && ( - { - setIsOpenBlockedUserModal(false) - }} /> - )} + + + {selectedDirect && !newChat && ( <> { onClick={async () => { try { setIsLoading(true) - if(isAlreadyBlocked === true){ - await removeBlockFromList(address, name) - } else if(isAlreadyBlocked === false) { - await addToBlockList(address, name) - } - executeEvent('updateChatMessagesWithBlocks', true) + executeEvent("blockUserFromOutside", { + user: address + }) + } catch (error) { console.error(error) } finally { diff --git a/src/qortalRequests.ts b/src/qortalRequests.ts index 82bbdf3..29704cd 100644 --- a/src/qortalRequests.ts +++ b/src/qortalRequests.ts @@ -1,5 +1,5 @@ import { banFromGroup, gateways, getApiKeyFromStorage } from "./background"; -import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellOrder, createBuyOrder, createGroupRequest, createPoll, createSellOrder, decryptAESGCMRequest, decryptData, decryptDataWithSharingKey, decryptQortalGroupData, deleteHostedData, deleteListItems, deployAt, encryptData, encryptDataWithSharingKey, encryptQortalGroupData, getArrrSyncStatus, getCrossChainServerInfo, getDaySummary, getForeignFee, getHostedData, getListItems, getNodeInfo, getNodeStatus, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getUserWalletTransactions, getWalletBalance, inviteToGroupRequest, joinGroup, kickFromGroupRequest, leaveGroupRequest, publishMultipleQDNResources, publishQDNResource, registerNameRequest, removeForeignServer, removeGroupAdminRequest, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, signTransaction, updateForeignFee, updateNameRequest, voteOnPoll } from "./qortalRequests/get"; +import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, buyNameRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellNameRequest, cancelSellOrder, createBuyOrder, createGroupRequest, createPoll, createSellOrder, decryptAESGCMRequest, decryptData, decryptDataWithSharingKey, decryptQortalGroupData, deleteHostedData, deleteListItems, deployAt, encryptData, encryptDataWithSharingKey, encryptQortalGroupData, getArrrSyncStatus, getCrossChainServerInfo, getDaySummary, getForeignFee, getHostedData, getListItems, getNodeInfo, getNodeStatus, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getUserWalletTransactions, getWalletBalance, inviteToGroupRequest, joinGroup, kickFromGroupRequest, leaveGroupRequest, publishMultipleQDNResources, publishQDNResource, registerNameRequest, removeForeignServer, removeGroupAdminRequest, saveFile, sellNameRequest, sendChatMessage, sendCoin, setCurrentForeignServer, showPdfReader, signTransaction, updateForeignFee, updateGroupRequest, updateNameRequest, voteOnPoll } from "./qortalRequests/get"; export const listOfAllQortalRequests = [ 'GET_USER_ACCOUNT', @@ -82,7 +82,11 @@ import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banF 'GET_NODE_INFO', 'GET_NODE_STATUS', 'GET_ARRR_SYNC_STATUS', - 'SHOW_PDF_READER' + 'SHOW_PDF_READER', + 'UPDATE_GROUP', + 'SELL_NAME', + 'CANCEL_SELL_NAME', + 'BUY_NAME' ] // Promisify chrome.storage.local.get @@ -850,6 +854,55 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { }); break; } + + case "UPDATE_GROUP" : { + const data = request.payload; + + updateGroupRequest(data, isFromExtension) + .then((res) => { + sendResponse(res); + }) + .catch((error) => { + sendResponse({ error: error.message }); + }); + break; + } + case "BUY_NAME" : { + const data = request.payload; + + buyNameRequest(data, isFromExtension) + .then((res) => { + sendResponse(res); + }) + .catch((error) => { + sendResponse({ error: error.message }); + }); + break; + } + case "SELL_NAME" : { + const data = request.payload; + + sellNameRequest(data, isFromExtension) + .then((res) => { + sendResponse(res); + }) + .catch((error) => { + sendResponse({ error: error.message }); + }); + break; + } + case "CANCEL_SELL_NAME" : { + const data = request.payload; + + cancelSellNameRequest(data, isFromExtension) + .then((res) => { + sendResponse(res); + }) + .catch((error) => { + sendResponse({ error: error.message }); + }); + break; + } } } diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 3c773a0..ee47e6f 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -28,7 +28,11 @@ import { makeAdmin, removeAdmin, cancelInvitationToGroup, - createGroup + createGroup, + updateGroup, + buyName, + cancelSellName, + sellName } from "../background"; import { decryptGroupEncryption, getNameInfo, uint8ArrayToObject } from "../backgroundFunctions/encryption"; import { QORT_DECIMALS } from "../constants/constants"; @@ -59,6 +63,7 @@ import utils from "../utils/utils"; import { RequestQueueWithPromise } from "../utils/queue/queue"; import { Sha256 } from "asmcrypto.js"; import ed2curve from "../deps/ed2curve"; +import { executeEvent } from "../utils/events"; export const requestQueueGetAtAddresses = new RequestQueueWithPromise(10); @@ -1183,6 +1188,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten failedPublishesIdentifiers.push({ reason: errorMsg, identifier: resource.identifier, + service: resource.service, }); continue; } @@ -1191,6 +1197,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten failedPublishesIdentifiers.push({ reason: errorMsg, identifier: resource.identifier, + service: resource.service, }); continue; } @@ -1247,6 +1254,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten failedPublishesIdentifiers.push({ reason: errorMsg, identifier: resource.identifier, + service: resource.service, }); continue; } @@ -1273,7 +1281,7 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten apiVersion: 2, withFee: true, }, - ], false); + ], true); await new Promise((res) => { setTimeout(() => { res(); @@ -1284,21 +1292,25 @@ export const publishMultipleQDNResources = async (data: any, sender, isFromExten failedPublishesIdentifiers.push({ reason: errorMsg, identifier: resource.identifier, + service: resource.service, }); } } catch (error) { failedPublishesIdentifiers.push({ reason: error?.message || "Unknown error", identifier: resource.identifier, + service: resource.service, }); } } if (failedPublishesIdentifiers.length > 0) { - const obj = {}; - obj["error"] = { - unsuccessfulPublishes: failedPublishesIdentifiers, - }; - return obj; + const obj = { + message: "Some resources have failed to publish.", + }; + obj["error"] = { + unsuccessfulPublishes: failedPublishesIdentifiers, + }; + return obj; } if(hasAppFee && checkbox1){ sendCoinFunc({ @@ -2682,7 +2694,8 @@ export const sendCoin = async (data, isFromExtension) => { text1: "Do you give this application permission to send coins?", text2: `To: ${recipient}`, highlightedText: `${amount} ${checkCoin}`, - fee: fee + fee: fee, + confirmCheckbox: true }, isFromExtension); const { accepted } = resPermission; @@ -3884,6 +3897,11 @@ export const registerNameRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const fee = await getFee("REGISTER_NAME"); const resPermission = await getUserPermission( { @@ -3897,7 +3915,7 @@ export const registerNameRequest = async (data, isFromExtension) => { const { accepted } = resPermission; if (accepted) { const name = data.name - const description = data?.description + const description = data?.description || "" const response = await registerName({ name, description }); return response @@ -3914,9 +3932,14 @@ export const updateNameRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const oldName = data.oldName const newName = data.newName - const description = data?.description + const description = data?.description || "" const fee = await getFee("UPDATE_NAME"); const resPermission = await getUserPermission( { @@ -3945,6 +3968,11 @@ export const leaveGroupRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId let groupInfo = null; try { @@ -3985,6 +4013,11 @@ export const inviteToGroupRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.inviteeAddress const inviteTime = data?.inviteTime @@ -4034,6 +4067,11 @@ export const kickFromGroupRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress const reason = data?.reason @@ -4083,6 +4121,11 @@ export const banFromGroupRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress const rBanTime = data?.banTime @@ -4133,6 +4176,11 @@ export const cancelGroupBanRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress @@ -4180,6 +4228,11 @@ export const addGroupAdminRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress @@ -4227,6 +4280,11 @@ export const removeGroupAdminRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress @@ -4274,6 +4332,11 @@ export const cancelGroupInviteRequest = async (data, isFromExtension) => { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupId = data.groupId const qortalAddress = data?.qortalAddress @@ -4368,15 +4431,20 @@ export const decryptAESGCMRequest = async (data, isFromExtension) => { }; export const createGroupRequest = async (data, isFromExtension) => { - const requiredFields = ["groupId", "qortalAddress"]; + const requiredFields = ["groupId", "qortalAddress", "groupName", "type", "approvalThreshold", "minBlock", "maxBlock"]; const missingFields: string[] = []; requiredFields.forEach((field) => { - if (!data[field]) { + if (data[field] !== undefined && data[field] !== null) { missingFields.push(field); } }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } const groupName = data.groupName - const description = data?.description + const description = data?.description || "" const type = +data.type const approvalThreshold = +data?.approvalThreshold const minBlock = +data?.minBlock @@ -4592,4 +4660,202 @@ export const getArrrSyncStatus = async () => { } catch (error) { throw new Error(error?.message || "Error in retrieving arrr sync status"); } +}; + +export const updateGroupRequest = async (data, isFromExtension) => { + const requiredFields = ["groupId", "newOwner", "type", "approvalThreshold", "minBlock", "maxBlock"]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } + const groupId = +data.groupId + const newOwner = data.newOwner + const description = data?.description || "" + const type = +data.type + const approvalThreshold = +data?.approvalThreshold + const minBlock = +data?.minBlock + const maxBlock = +data.maxBlock + + let groupInfo = null; + try { + const url = await createEndpoint(`/groups/${groupId}`); + const response = await fetch(url); + if (!response.ok) throw new Error("Failed to fetch group"); + + groupInfo = await response.json(); + } catch (error) { + const errorMsg = (error && error.message) || "Group not found"; + throw new Error(errorMsg); + } + + const displayInvitee = await getNameInfoForOthers(newOwner) + + + const fee = await getFee("CREATE_GROUP"); + const resPermission = await getUserPermission( + { + text1: `Do you give this application permission to update this group?`, + text2: `New owner: ${displayInvitee || newOwner}`, + highlightedText: `Group: ${groupInfo.groupName}`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await updateGroup({ + groupId, + newOwner, + newIsOpen: type, + newDescription: description, + newApprovalThreshold: approvalThreshold, + newMinimumBlockDelay: minBlock, + newMaximumBlockDelay: maxBlock + }) + return response + + } else { + throw new Error("User declined request"); + } +}; + + +export const sellNameRequest = async (data, isFromExtension) => { + const requiredFields = ["salePrice", "nameForSale"]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } + const name = data.nameForSale + const sellPrice = +data.salePrice + + const validApi = await getBaseApi(); + + const response = await fetch(validApi + "/names/" + name); + const nameData = await response.json(); +if(!nameData) throw new Error("This name does not exist") + +if(nameData?.isForSale) throw new Error("This name is already for sale") + const fee = await getFee("SELL_NAME"); + const resPermission = await getUserPermission( + { + text1: `Do you give this application permission to create a sell name transaction?`, + highlightedText: `Sell ${name} for ${sellPrice} QORT`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await sellName({ + name, + sellPrice + }) + return response + + } else { + throw new Error("User declined request"); + } +}; + +export const cancelSellNameRequest = async (data, isFromExtension) => { + const requiredFields = ["nameForSale"]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } + const name = data.nameForSale + const validApi = await getBaseApi(); + + const response = await fetch(validApi + "/names/" + name); + const nameData = await response.json(); +if(!nameData?.isForSale) throw new Error("This name is not for sale") + + const fee = await getFee("CANCEL_SELL_NAME"); + const resPermission = await getUserPermission( + { + text1: `Do you give this application permission to cancel the selling of a name?`, + highlightedText: `Name: ${name}`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await cancelSellName({ + name + }) + return response + + } else { + throw new Error("User declined request"); + } +}; + +export const buyNameRequest = async (data, isFromExtension) => { + const requiredFields = ["nameForSale"]; + const missingFields: string[] = []; + requiredFields.forEach((field) => { + if (data[field] !== undefined && data[field] !== null) { + missingFields.push(field); + } + }); + if (missingFields.length > 0) { + const missingFieldsString = missingFields.join(", "); + const errorMsg = `Missing fields: ${missingFieldsString}`; + throw new Error(errorMsg); + } + const name = data.nameForSale + + const validApi = await getBaseApi(); + + const response = await fetch(validApi + "/names/" + name); + const nameData = await response.json(); + if(!nameData?.isForSale) throw new Error("This name is not for sale") + const sellerAddress = nameData.owner + const sellPrice = +nameData.salePrice + + + const fee = await getFee("BUY_NAME"); + const resPermission = await getUserPermission( + { + text1: `Do you give this application permission to buy a name?`, + highlightedText: `Buying ${name} for ${sellPrice} QORT`, + fee: fee.fee, + }, + isFromExtension + ); + const { accepted } = resPermission; + if (accepted) { + const response = await buyName({ + name, + sellerAddress, + sellPrice + }) + return response + + } else { + throw new Error("User declined request"); + } }; \ No newline at end of file diff --git a/src/transactions/BuyNameTransacion.ts b/src/transactions/BuyNameTransacion.ts new file mode 100644 index 0000000..8c321d3 --- /dev/null +++ b/src/transactions/BuyNameTransacion.ts @@ -0,0 +1,45 @@ +// @ts-nocheck + +import { QORT_DECIMALS } from "../constants/constants" +import TransactionBase from "./TransactionBase" + + +export default class BuyNameTransacion extends TransactionBase { + constructor() { + super() + this.type = 7 + } + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + + set name(name) { + this.nameText = name + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) + this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) + } + + set sellPrice(sellPrice) { + this._sellPrice = sellPrice * QORT_DECIMALS + this._sellPriceBytes = this.constructor.utils.int64ToBytes(this._sellPrice) + } + + set recipient(recipient) { + this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient) + this.theRecipient = recipient + } + + get params() { + const params = super.params + params.push( + this._nameLength, + this._nameBytes, + this._sellPriceBytes, + this._recipient, + this._feeBytes + ) + return params + } +} diff --git a/src/transactions/CancelSellNameTransacion.ts b/src/transactions/CancelSellNameTransacion.ts new file mode 100644 index 0000000..04296c8 --- /dev/null +++ b/src/transactions/CancelSellNameTransacion.ts @@ -0,0 +1,33 @@ +// @ts-nocheck + +import { QORT_DECIMALS } from "../constants/constants" +import TransactionBase from "./TransactionBase" + + +export default class CancelSellNameTransacion extends TransactionBase { + constructor() { + super() + this.type = 6 + } + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + + set name(name) { + this.nameText = name + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) + this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) + } + + get params() { + const params = super.params + params.push( + this._nameLength, + this._nameBytes, + this._feeBytes + ) + return params + } +} diff --git a/src/transactions/SellNameTransacion.ts b/src/transactions/SellNameTransacion.ts new file mode 100644 index 0000000..3926568 --- /dev/null +++ b/src/transactions/SellNameTransacion.ts @@ -0,0 +1,40 @@ +// @ts-nocheck + +import { QORT_DECIMALS } from "../constants/constants" +import TransactionBase from "./TransactionBase" + + +export default class SellNameTransacion extends TransactionBase { + constructor() { + super() + this.type = 5 + } + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + + set name(name) { + this.nameText = name + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) + this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) + } + + set sellPrice(sellPrice) { + this.showSellPrice = sellPrice + this._sellPrice = sellPrice * QORT_DECIMALS + this._sellPriceBytes = this.constructor.utils.int64ToBytes(this._sellPrice) + } + + get params() { + const params = super.params + params.push( + this._nameLength, + this._nameBytes, + this._sellPriceBytes, + this._feeBytes + ) + return params + } +} diff --git a/src/transactions/UpdateGroupTransaction.ts b/src/transactions/UpdateGroupTransaction.ts new file mode 100644 index 0000000..9d9856b --- /dev/null +++ b/src/transactions/UpdateGroupTransaction.ts @@ -0,0 +1,62 @@ +// @ts-nocheck + + +import { QORT_DECIMALS } from "../constants/constants"; +import TransactionBase from "./TransactionBase"; + +export default class UpdateGroupTransaction extends TransactionBase { + constructor() { + super() + this.type = 23 + } + + + + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + set newOwner(newOwner) { + this._newOwner = newOwner instanceof Uint8Array ? newOwner : this.constructor.Base58.decode(newOwner) + } + set newIsOpen(newIsOpen) { + + this._rGroupType = new Uint8Array(1) + this._rGroupType[0] = newIsOpen + } + set newDescription(newDescription) { + this._rGroupDescBytes = this.constructor.utils.stringtoUTF8Array(newDescription.toLocaleLowerCase()) + this._rGroupDescLength = this.constructor.utils.int32ToBytes(this._rGroupDescBytes.length) + } + set newApprovalThreshold(newApprovalThreshold) { + this._rGroupApprovalThreshold = new Uint8Array(1) + this._rGroupApprovalThreshold[0] = newApprovalThreshold; + } + set newMinimumBlockDelay(newMinimumBlockDelay) { + this._rGroupMinimumBlockDelayBytes = this.constructor.utils.int32ToBytes(newMinimumBlockDelay) + } + set newMaximumBlockDelay(newMaximumBlockDelay) { + + this._rGroupMaximumBlockDelayBytes = this.constructor.utils.int32ToBytes(newMaximumBlockDelay) + } + + set _groupId(_groupId){ + this._groupBytes = this.constructor.utils.int32ToBytes(_groupId) + } + get params() { + const params = super.params + params.push( + this._groupBytes, + this._newOwner, + this._rGroupDescLength, + this._rGroupDescBytes, + this._rGroupType, + this._rGroupApprovalThreshold, + this._rGroupMinimumBlockDelayBytes, + this._rGroupMaximumBlockDelayBytes, + this._feeBytes + ) + return params + } +} \ No newline at end of file diff --git a/src/transactions/transactions.ts b/src/transactions/transactions.ts index db516a1..02cdc59 100644 --- a/src/transactions/transactions.ts +++ b/src/transactions/transactions.ts @@ -20,17 +20,25 @@ import DeployAtTransaction from './DeployAtTransaction.js' import RewardShareTransaction from './RewardShareTransaction.js' import RemoveRewardShareTransaction from './RemoveRewardShareTransaction.js' import UpdateNameTransaction from './UpdateNameTransaction.js' +import SellNameTransacion from './SellNameTransacion.js' +import CancelSellNameTransacion from './CancelSellNameTransacion.js' +import BuyNameTransacion from './BuyNameTransacion.js' +import UpdateGroupTransaction from './UpdateGroupTransaction.js' export const transactionTypes = { 3: RegisterNameTransaction, 4: UpdateNameTransaction, 2: PaymentTransaction, + 5: SellNameTransacion, + 6: CancelSellNameTransacion, + 7: BuyNameTransacion, 8: CreatePollTransaction, 9: VoteOnPollTransaction, 16: DeployAtTransaction, 18: ChatTransaction, 181: GroupChatTransaction, 22: CreateGroupTransaction, + 23: UpdateGroupTransaction, 24: AddGroupAdminTransaction, 25: RemoveGroupAdminTransaction, 26: GroupBanTransaction,