From 7cc76f018f03d7ec674e8cb8a705a95539ef999a Mon Sep 17 00:00:00 2001 From: AzizbekFayziyev Date: Sat, 21 Jun 2025 16:33:14 +0500 Subject: [PATCH] add: BURN_ASSET method --- src/app/App.tsx | 68 ++-- .../OuterConfirmation/OuterConfirmation.tsx | 293 ++++++++++++------ src/app/utils/utils.ts | 26 +- src/background/background.ts | 55 +++- src/background/wallet.ts | 50 ++- src/constants/index.ts | 3 +- src/types/index.ts | 96 ++++++ 7 files changed, 438 insertions(+), 153 deletions(-) create mode 100644 src/types/index.ts diff --git a/src/app/App.tsx b/src/app/App.tsx index ddfe106..de92113 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -41,49 +41,11 @@ import OuterConfirmation from "./components/OuterConfirmation/OuterConfirmation" import Formatters from "./utils/formatters"; import Big from "big.js"; import swapModalStyles from "./styles/SwapModal.module.scss"; -import useGetAsset from "./hooks/useGetAsset"; - -// Types -type dispatchType = () => void; -type destinationsType = { address: string, amount: number }[]; -type transferType = { transfer: { sender: string, destination: string, destinations: destinationsType, amount: string, asset: { ticker: string }, comment?: string }, id: number }; -type RequestType = { method: string; assetId: string, amount: string, destinationAddress: string, destinationChainId: string }; -type SwapRequest = { - id: string; - swap: { - destinationAddress: string; - destinationAsset: string; - destinationAssetAmount: string; - currentAsset: string; - currentAssetAmount: string; - }; -}; -type SwapProposal = { - to_finalizer: { amount: Big }[]; - to_initiator: { amount: Big }[]; -}; -type Asset = { - decimal_point: number; - [key: string]: any; -}; -type AcceptSwapReq = { - id: string; - hex_raw_proposal: string; - swapProposal: SwapProposal; - receivingAsset: Asset; - sendingAsset: Asset; -}; - -type AssetWhitelistReq = { - id: string; - asset_id: string; - asset_name: string; -} +import { AcceptSwapReq, AssetWhitelistReq, dispatchType, RequestType, SwapRequest, transferType } from "../types"; function App() { const { state, dispatch } = useContext(Store); const [confirmationModalOpen, setConfirmationModalOpen] = useState(false); - const { getAssetById } = useGetAsset(); const [incorrectPassword, setIncorrectPassword] = useState(false); const [loggedIn, setLoggedIn] = useState(false); @@ -92,11 +54,6 @@ function App() { const [connectOpened, setConnectOpened] = useState(false); - // Flags of display - // creatingPassword flag has an effect only in case of loggedIn flag is false. - // creatingPassword flag means whether to show the password create screen or existing password enter screen. - // const creatingPassword = !passwordExists(); - useEffect(() => { async function loadLogin() { const password = await getSessionPassword(); @@ -560,6 +517,29 @@ function App() { } } + async function getBurnAssetRequests() { + const response = await fetchBackground({ method: "GET_BURN_ASSET_REQUESTS" }); + const burnRequests = response.data; + + const pageReqs = burnRequests.map((e: any) => { + const data = e.burn; + + return { + id: e.id, + method: "FINALIZE_BURN_ASSET_REQUEST", + name: "BURN_ASSET", + params: [ + data + ], + }; + }); + + if (pageReqs.length > 0) { + goTo(OuterConfirmation, { reqs: pageReqs }); + } + } + + await getBurnAssetRequests(); await getAliasCreationRequests(); await getIonicSwapRequests(); await getSignRequests(); diff --git a/src/app/components/OuterConfirmation/OuterConfirmation.tsx b/src/app/components/OuterConfirmation/OuterConfirmation.tsx index 5892e72..f6580e2 100644 --- a/src/app/components/OuterConfirmation/OuterConfirmation.tsx +++ b/src/app/components/OuterConfirmation/OuterConfirmation.tsx @@ -2,7 +2,7 @@ import React from "react"; import Button, { ButtonThemes } from "../UI/Button/Button"; import styles from "./OuterConfirmation.module.scss"; import { useState, useEffect } from "react"; -import { fetchBackground } from "../../utils/utils"; +import { fetchBackground, shortenAddress } from "../../utils/utils"; import customTokenIcon from "../../assets/tokens-svg/custom-token.svg"; import banditIcon from "../../assets/tokens-svg/bandit-icon.svg"; import zanoIcon from "../../assets/tokens-svg/zano.svg"; @@ -11,6 +11,8 @@ import ethIcon from "../../assets/tokens-svg/eth.svg"; import arrowIcon from "../../assets/svg/arrow-blue.svg"; import InfoTooltip from "../UI/InfoTooltip"; import { getCurrent, goBack } from "react-chrome-extension-router"; +import { BurnAssetDataType } from "../../../types"; +import { BANDIT_ASSET_ID, ZANO_ASSET_ID } from "../../../constants"; interface ParamsType { key: number; @@ -29,13 +31,14 @@ const OuterConfirmation = () => { const [reqIndex, setReqIndex] = useState(0); const [accepting, setAccepting] = useState(false); const [denying, setDenying] = useState(false); - const [showFullAddresses, setShowFullAddresses] = useState(false); + const [showFullItems, setShowFullItems] = useState(false); const [showFullComment, setShowFullComment] = useState(false); const req = reqs[reqIndex] || {}; const { id, name, params, method, destinations } = req; const isTransferMethod = name?.toLowerCase() === "transfer"; + const isBurnMethod = name?.toLowerCase() === "burn_asset"; const isMultipleDestinations = destinations && destinations.length > 0; @@ -56,7 +59,6 @@ const OuterConfirmation = () => { } } - async function acceptClick() { setAccepting(true); await fetchBackground({ method, id, success: true }); @@ -88,7 +90,188 @@ const OuterConfirmation = () => { const disabled = accepting || denying; - console.log("FINALIZA TRANSACTION", req); + const getConfirmationName = () => { + if (isTransferMethod) { + return "Please confirm the transfer details"; + } else if (isBurnMethod) { + return "BURN ASSET" + } else { + return name + } + } + + const getConfirmationContent = () => { + if (isTransferMethod) { + return ( + <> +
+
+
From
+

{transactionParams?.F}

+
+
+
Asset
+

{getAssetIcon(transactionParams?.Asset)} {transactionParams?.Asset}

+
+
+
Amount
+

{totalAmount}

+
+ +
+
Comment
+

{(transactionParams?.Comment?.length > 60 && !showFullComment) ? + <> + {transactionParams?.Comment?.slice(0, 60)}... + + + : + <> + {transactionParams?.Comment} + {showFullComment && } + + }

+
+
+ +
+
+
To
+

{isMultipleDestinations ? <>{destinations?.length} addresses : transactionParams?.To}

+
+ + {!isMultipleDestinations &&
+
Amount
+

{totalAmount}

+
} +
+ + {isMultipleDestinations && ( + <> + + + {showFullItems && destinations?.map((item: DestionationType, idx: number) => ( +
+

RECIPIENT {idx + 1}

+ +
+
+
To
+

{item.address}

+
+ +
+
Amount
+

{item.amount}

+
+
+
+ ))} + + )} + + ) + } else if (isBurnMethod) { + const { + assetId, + burnAmount, + nativeAmount, + pointTxToAddress, + serviceEntries + }: BurnAssetDataType = params[0]; + const getIconByAsseetId = () => { + if (assetId === ZANO_ASSET_ID) { + return "ZANO" + } else if (assetId === BANDIT_ASSET_ID) { + return "BANDIT" + } else { + return assetId + } + } + + return ( + <> +
+
+
Asset
+

{getAssetIcon(getIconByAsseetId())} {shortenAddress(assetId, 6, 6)}

+
+
+
Burn Amount
+

{burnAmount}

+
+ {nativeAmount &&
+
Native Amount
+

{nativeAmount}

+
} + {pointTxToAddress &&
+
Send Tx To
+

{shortenAddress(pointTxToAddress, 6, 6)}

+
} +
+ + {serviceEntries && } + + {showFullItems && serviceEntries?.map((item, idx) => { + const dataLength = serviceEntries?.length || 1; + + return
+ {dataLength > 1 &&

+ Service Entries {idx + 1} +

} + +
+
+
Body
+

{shortenAddress(item.body, 6, 6)}

+
+
+
Flags
+

{item.flags}

+
+
+
Instruction
+

{item.instruction}

+
+ {item.security &&
+
Security
+

{shortenAddress(item.security, 6, 6)}

+
} +
+
Service Id
+

{item.service_id}

+
+
+
+ })} + + ); + } else { + return ( +
+
+ {Array.isArray(params) && params?.map((item: ParamsType, idx: number) => ( +
+
{item.key}
+

{item.value}

+
+ ))} +
+
+ ) + } + } + if (!req) { return
No request found.
; @@ -98,99 +281,15 @@ const OuterConfirmation = () => {

Request Confirmation

- {isTransferMethod ? "Please confirm the transfer details" : name} + {getConfirmationName()}
- {isTransferMethod ? ( - <> -
-
-
From
-

{transactionParams?.From}

-
-
-
Asset
-

{getAssetIcon(transactionParams?.Asset)} {transactionParams?.Asset}

-
-
-
Amount
-

{totalAmount}

-
- -
-
Comment
-

{(transactionParams?.Comment?.length > 60 && !showFullComment) ? - <> - {transactionParams?.Comment?.slice(0, 60)}... - - - : - <> - {transactionParams?.Comment} - {showFullComment && } - - }

-
-
- -
-
-
To
-

{isMultipleDestinations ? <>{destinations?.length} addresses : transactionParams?.To}

-
- - {!isMultipleDestinations &&
-
Amount
-

{totalAmount}

-
} -
- - {isMultipleDestinations && ( - <> - - - {showFullAddresses && destinations?.map((item: DestionationType, idx: number) => ( -
-

RECIPIENT {idx + 1}

- -
-
-
To
-

{item.address}

-
- -
-
Amount
-

{item.amount}

-
-
-
- ))} - - )} - - ) : ( -
-
- {Array.isArray(params) && params?.map((item: ParamsType, idx: number) => ( -
-
{item.key}
-

{item.value}

-
- ))} -
-
- )} + {getConfirmationContent()}
- {isTransferMethod && <> + {isTransferMethod || isBurnMethod && <>
Transaction fee @@ -198,12 +297,14 @@ const OuterConfirmation = () => {

0.01 ZANO

-
+ {isTransferMethod && <> +
-
-
Total
-

{totalAmount}

-
+
+
Total
+

{totalAmount}

+
+ } }
diff --git a/src/app/utils/utils.ts b/src/app/utils/utils.ts index b9493a4..c04426e 100644 --- a/src/app/utils/utils.ts +++ b/src/app/utils/utils.ts @@ -12,12 +12,12 @@ interface ValidationResult { error?: string; } -export async function fetchBackground(data: { - method: string; - password?: string; - id?: number; - success?: boolean; - credentials?: { port: string }; +export async function fetchBackground(data: { + method: string; + password?: string; + id?: number; + success?: boolean; + credentials?: { port: string }; alias?: string; }): Promise { return new Promise((resolve, reject) => { @@ -107,7 +107,7 @@ export function validateTokensInput(input: string | number, decimal_point: numbe const roundedInput = new Decimal(dotInput).toFixed(1); if (roundedInput.replace(/./g, '').length <= 20) { - return roundedInput; + return roundedInput; } } @@ -144,3 +144,15 @@ export function validateTokensInput(input: string | number, decimal_point: numbe valid: true }; } + + +export const shortenAddress = ( + address: string | undefined, + startAmount: number = 5, + endAmount: number = 3 +) => { + if (!address) { + return ""; + } + return address.slice(0, startAmount) + "..." + address.slice(-endAmount); +}; \ No newline at end of file diff --git a/src/background/background.ts b/src/background/background.ts index b25b9bf..ccaf701 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -15,7 +15,8 @@ import { getWhiteList, getAssetInfo, createAlias, - addAssetToWhitelist + addAssetToWhitelist, + burnAsset } from "./wallet"; import JSONbig from "json-bigint"; @@ -232,14 +233,15 @@ const signReqFinalizers: SignReqFinalizer = {}; const signReqs: unknown[] = []; const savedRequests: Record< - "IONIC_SWAP" | "ACCEPT_IONIC_SWAP" | "CREATE_ALIAS" | "TRANSFER" | "ASSETS_WHITELIST_ADD", + "IONIC_SWAP" | "ACCEPT_IONIC_SWAP" | "CREATE_ALIAS" | "TRANSFER" | "ASSETS_WHITELIST_ADD" | "BURN_ASSET", Record > = { IONIC_SWAP: {}, ACCEPT_IONIC_SWAP: {}, CREATE_ALIAS: {}, TRANSFER: {}, - ASSETS_WHITELIST_ADD: {} + ASSETS_WHITELIST_ADD: {}, + BURN_ASSET: {}, }; const allPopupIds: number[] = []; @@ -280,7 +282,9 @@ const SELF_ONLY_REQUESTS = [ "GET_WALLETS", "FINALIZE_TRANSFER_REQUEST", "GET_ASSETS_WHITELIST_ADD_REQUESTS", - "FINALIZE_ASSETS_WHITELIST_ADD_REQUESTS" + "FINALIZE_ASSETS_WHITELIST_ADD_REQUESTS", + "GET_BURN_ASSET_REQUESTS", + "FINALIZE_BURN_ASSET_REQUEST", ]; chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { @@ -321,6 +325,10 @@ interface RequestType { asset_id?: string; asset_name?: string; comment: string; + burnAmount: number; + nativeAmount?: number; + pointTxToAddress?: string; + serviceEntries?: any[]; } interface Sender { @@ -877,6 +885,45 @@ async function processRequest(request: RequestType, sender: Sender, sendResponse ) break } + case "BURN_ASSET": + PopupRequestsMethods.onRequestCreate( + "BURN_ASSET", + request, + sendResponse, + { + method: "FINALIZE_BURN_ASSET_REQUEST", + name: "Burn asset", + burn: request, + } as any + ); + break; + + case "GET_BURN_ASSET_REQUESTS": + PopupRequestsMethods.getRequestsList("BURN_ASSET", sendResponse); + break; + + case "FINALIZE_BURN_ASSET_REQUEST": + PopupRequestsMethods.onRequestFinalize( + "BURN_ASSET", + request, + sendResponse, + async (req) => { + const burnReq = req.burn as any; + return burnAsset({ + assetId: burnReq.assetId, + burnAmount: burnReq.burnAmount, + nativeAmount: burnReq.nativeAmount, + pointTxToAddress: burnReq.pointTxToAddress, + serviceEntries: burnReq.serviceEntries, + }); + }, + { + console: "Burn asset error:", + response: "Failed to burn asset", + reqNotFound: "Burn asset request not found", + } + ); + break; default: diff --git a/src/background/wallet.ts b/src/background/wallet.ts index 48e9625..beb164d 100644 --- a/src/background/wallet.ts +++ b/src/background/wallet.ts @@ -511,4 +511,52 @@ export async function addAssetToWhitelist(assetId: string) { const data = await response.json(); return data; -} \ No newline at end of file +} + +export const burnAsset = async ({ + assetId, + burnAmount, + decimalPoint = 12, + nativeAmount = 0, + pointTxToAddress, + serviceEntries = [], +}: { + assetId: string; + burnAmount: number; + decimalPoint?: number; + nativeAmount?: number; + pointTxToAddress?: string; + serviceEntries?: { + body: string; + flags: number; + instruction: string; + security?: string; + service_id: string; + }[]; +}) => { + const params: any = { + asset_id: assetId, + burn_amount: addZeros(burnAmount, decimalPoint).toFixed(0), + }; + + if (nativeAmount) { + params.native_amount = nativeAmount; + } + + if (pointTxToAddress) { + params.point_tx_to_address = pointTxToAddress; + } + + if (serviceEntries.length > 0) { + params.service_entries = serviceEntries; + } + + const response = await fetchData("burn_asset", params); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + return data; +}; \ No newline at end of file diff --git a/src/constants/index.ts b/src/constants/index.ts index 8f720b4..6cdc915 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1 +1,2 @@ -export const ZANO_ASSET_ID = "d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a"; \ No newline at end of file +export const ZANO_ASSET_ID = "d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a"; +export const BANDIT_ASSET_ID = "55a8e0a730b133fb83915ba0e4335a680ae9d07a99642b17774460560f3b003d"; \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..f1600fd --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,96 @@ +// Types +export type dispatchType = () => void; +export type destinationsType = { address: string, amount: number }[]; + +export type transferType = { + transfer: + { + sender: string, + destination: string, + destinations: destinationsType, + amount: string, + asset: { + ticker: string + }, + comment?: string, + }, id: number +}; + +export type RequestType = { + method: string; + assetId: string, + amount: string, + destinationAddress: string, + destinationChainId: string, + burnAmount: string; + nativeAmount?: number; + pointTxToAddress?: string; + serviceEntries?: any[]; +}; + +export type SwapRequest = { + id: string; + swap: { + destinationAddress: string; + destinationAsset: string; + destinationAssetAmount: string; + currentAsset: string; + currentAssetAmount: string; + }; +}; + +export type SwapProposal = { + to_finalizer: { amount: Big }[]; + to_initiator: { amount: Big }[]; +}; + +export type Asset = { + decimal_point: number; + [key: string]: any; +}; + +export type AcceptSwapReq = { + id: string; + hex_raw_proposal: string; + swapProposal: SwapProposal; + receivingAsset: Asset; + sendingAsset: Asset; +}; + +export type AssetWhitelistReq = { + id: string; + asset_id: string; + asset_name: string; +} + +export interface BurnAssetRequest { + params: { + assetId: string; + burnAmount: number; + nativeAmount?: number; + pointTxToAddress?: string; + serviceEntries?: { + body: string; + flags: number; + instruction: string; + security?: string; + service_id: string; + }[]; + }; + [key: string]: any; +} + +export interface BurnAssetDataType { + assetId: string; + burnAmount: number; + decimalPoint?: number; + nativeAmount?: number; + pointTxToAddress?: string; + serviceEntries?: { + service_id: string; + instruction: string; + body: string; + flags: number; + security?: string; + }[]; +} \ No newline at end of file