diff --git a/src/qortal/get.ts b/src/qortal/get.ts
index 102a9bf..dad01f5 100644
--- a/src/qortal/get.ts
+++ b/src/qortal/get.ts
@@ -38,7 +38,11 @@ import {
getPublicKey,
transferAsset,
} from '../background/background.ts';
-import { getNameInfo, uint8ArrayToObject } from '../encryption/encryption.ts';
+import {
+ getAllUserNames,
+ getNameInfo,
+ uint8ArrayToObject,
+} from '../encryption/encryption.ts';
import { showSaveFilePicker } from '../hooks/useQortalMessageListener.tsx';
import { getPublishesFromAdminsAdminSpace } from '../components/Chat/AdminSpaceInner.tsx';
import { extractComponents } from '../components/Chat/MessageDisplay.tsx';
@@ -1315,7 +1319,7 @@ export const publishQDNResource = async (
if (appFee && appFee > 0 && appFeeRecipient) {
hasAppFee = true;
}
- const registeredName = await getNameInfo();
+ const registeredName = data?.name || (await getNameInfo());
const name = registeredName;
if (!name) {
throw new Error(
@@ -1331,7 +1335,7 @@ export const publishQDNResource = async (
const title = data.title;
const description = data.description;
const category = data.category;
-
+ const file = data?.file || data?.blob;
const tags = data?.tags || [];
const result = {};
@@ -1346,9 +1350,7 @@ export const publishQDNResource = async (
if (data.identifier == null) {
identifier = 'default';
}
- if (data?.file || data?.blob) {
- data64 = await fileToBase64(data?.file || data?.blob);
- }
+
if (
data.encrypt &&
(!data.publicKeys ||
@@ -1367,6 +1369,9 @@ export const publishQDNResource = async (
const parsedData = resKeyPair;
const privateKey = parsedData.privateKey;
const userPublicKey = parsedData.publicKey;
+ if (data?.file || data?.blob) {
+ data64 = await fileToBase64(data?.file || data?.blob);
+ }
const encryptDataResponse = encryptDataGroup({
data64,
publicKeys: data.publicKeys,
@@ -1410,6 +1415,7 @@ export const publishQDNResource = async (
}),
text2: `service: ${service}`,
text3: `identifier: ${identifier || null}`,
+ text4: `name: ${registeredName}`,
fee: fee.fee,
...handleDynamicValues,
},
@@ -1420,11 +1426,10 @@ export const publishQDNResource = async (
try {
const resPublish = await publishData({
registeredName: encodeURIComponent(name),
- file: data64,
+ data: data64 ? data64 : file,
service: service,
identifier: encodeURIComponent(identifier),
- uploadType: 'file',
- isBase64: true,
+ uploadType: data64 ? 'base64' : 'file',
filename: filename,
title,
description,
@@ -1558,6 +1563,7 @@ export const publishMultipleQDNResources = async (
const fee = await getFee('ARBITRARY');
const registeredName = await getNameInfo();
+
const name = registeredName;
if (!name) {
@@ -1568,6 +1574,14 @@ export const publishMultipleQDNResources = async (
);
}
+ const userNames = await getAllUserNames();
+ data.resources?.forEach((item) => {
+ if (item?.name && !userNames?.includes(item.name))
+ throw new Error(
+ `The name ${item.name}, does not belong to the publisher.`
+ );
+ });
+
const appFee = data?.appFee ? +data.appFee : undefined;
const appFeeRecipient = data?.appFeeRecipient;
let hasAppFee = false;
@@ -1638,7 +1652,7 @@ export const publishMultipleQDNResources = async (
Service: ${
resource.service
}
- Name: ${name}
+ Name: ${resource?.name || name}
Identifier: ${
resource.identifier
}
@@ -1695,6 +1709,7 @@ export const publishMultipleQDNResources = async (
reason: errorMsg,
identifier: resource.identifier,
service: resource.service,
+ name: resource?.name || name,
});
continue;
}
@@ -1709,12 +1724,13 @@ export const publishMultipleQDNResources = async (
reason: errorMsg,
identifier: resource.identifier,
service: resource.service,
+ name: resource?.name || name,
});
continue;
}
const service = resource.service;
let identifier = resource.identifier;
- let data64 = resource?.data64 || resource?.base64;
+ let rawData = resource?.data64 || resource?.base64;
const filename = resource.filename;
const title = resource.title;
const description = resource.description;
@@ -1741,26 +1757,31 @@ export const publishMultipleQDNResources = async (
reason: errorMsg,
identifier: resource.identifier,
service: resource.service,
+ name: resource?.name || name,
});
continue;
}
if (resource.file) {
- data64 = await fileToBase64(resource.file);
+ rawData = resource.file;
}
+
if (resourceEncrypt) {
try {
+ if (resource?.file) {
+ rawData = await fileToBase64(resource.file);
+ }
const resKeyPair = await getKeyPair();
const parsedData = resKeyPair;
const privateKey = parsedData.privateKey;
const userPublicKey = parsedData.publicKey;
const encryptDataResponse = encryptDataGroup({
- data64,
+ data64: rawData,
publicKeys: data.publicKeys,
privateKey,
userPublicKey,
});
if (encryptDataResponse) {
- data64 = encryptDataResponse;
+ rawData = encryptDataResponse;
}
} catch (error) {
const errorMsg =
@@ -1772,32 +1793,37 @@ export const publishMultipleQDNResources = async (
reason: errorMsg,
identifier: resource.identifier,
service: resource.service,
+ name: resource?.name || name,
});
continue;
}
}
try {
+ const dataType =
+ resource?.base64 || resource?.data64 || resourceEncrypt
+ ? 'base64'
+ : 'file';
+ console.log('dataType', dataType);
await retryTransaction(
publishData,
[
{
- registeredName: encodeURIComponent(name),
- file: data64,
- service: service,
- identifier: encodeURIComponent(identifier),
- uploadType: 'file',
- isBase64: true,
- filename: filename,
- title,
- description,
+ apiVersion: 2,
category,
+ data: rawData,
+ description,
+ filename: filename,
+ identifier: encodeURIComponent(identifier),
+ registeredName: encodeURIComponent(resource?.name || name),
+ service: service,
tag1,
tag2,
tag3,
tag4,
tag5,
- apiVersion: 2,
+ title,
+ uploadType: dataType,
withFee: true,
},
],
@@ -1818,6 +1844,7 @@ export const publishMultipleQDNResources = async (
reason: errorMsg,
identifier: resource.identifier,
service: resource.service,
+ name: resource?.name || name,
});
}
} catch (error) {
@@ -1829,6 +1856,7 @@ export const publishMultipleQDNResources = async (
}),
identifier: resource.identifier,
service: resource.service,
+ name: resource?.name || name,
});
}
}
@@ -2324,6 +2352,44 @@ export const joinGroup = async (data, isFromExtension) => {
export const saveFile = async (data, sender, isFromExtension, snackMethods) => {
try {
+ if (!data?.filename) throw new Error('Missing filename');
+ if (data?.location) {
+ const requiredFieldsLocation = ['service', 'name'];
+ const missingFieldsLocation: string[] = [];
+ requiredFieldsLocation.forEach((field) => {
+ if (!data?.location[field]) {
+ missingFieldsLocation.push(field);
+ }
+ });
+ if (missingFieldsLocation.length > 0) {
+ const missingFieldsString = missingFieldsLocation.join(', ');
+ const errorMsg = `Missing fields: ${missingFieldsString}`;
+ throw new Error(errorMsg);
+ }
+ const resPermission = await getUserPermission(
+ {
+ text1: 'Would you like to download:',
+ highlightedText: `${data?.filename}`,
+ },
+ isFromExtension
+ );
+ const { accepted } = resPermission;
+ if (!accepted) throw new Error('User declined to save file');
+ const a = document.createElement('a');
+ let locationUrl = `/arbitrary/${data.location.service}/${data.location.name}`;
+ if (data.location.identifier) {
+ locationUrl = locationUrl + `/${data.location.identifier}`;
+ }
+ const endpoint = await createEndpoint(
+ locationUrl + `?attachment=true&attachmentFilename=${data?.filename}`
+ );
+ a.href = endpoint;
+ a.download = data.filename;
+ document.body.appendChild(a);
+ a.click();
+ a.remove();
+ return true;
+ }
const requiredFields = ['filename', 'blob'];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
@@ -2341,6 +2407,8 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => {
}
const filename = data.filename;
const blob = data.blob;
+
+ const mimeType = blob.type || data.mimeType;
const resPermission = await getUserPermission(
{
text1: i18n.t('question:download_file', {
@@ -2351,6 +2419,17 @@ export const saveFile = async (data, sender, isFromExtension, snackMethods) => {
isFromExtension
);
const { accepted } = resPermission;
+ if (!accepted) throw new Error('User declined to save file'); // TODO translate
+ showSaveFilePicker(
+ {
+ filename,
+ mimeType,
+ blob,
+ },
+ snackMethods
+ );
+
+ return true;
if (accepted) {
const mimeType = blob.type || data.mimeType;
@@ -5396,11 +5475,10 @@ export const updateNameRequest = async (data, isFromExtension) => {
const fee = await getFee('UPDATE_NAME');
const resPermission = await getUserPermission(
{
- text1: i18n.t('question:permission.register_name', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: data.newName,
- text2: data?.description,
+ text1: `Do you give this application permission to update this name?`, // TODO translate
+ text2: `previous name: ${oldName}`,
+ text3: `new name: ${newName}`,
+ text4: data?.description,
fee: fee.fee,
},
isFromExtension
@@ -6012,7 +6090,7 @@ export const createGroupRequest = async (data, isFromExtension) => {
];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
- if (data[field] !== undefined && data[field] !== null) {
+ if (data[field] === undefined || data[field] === null) {
missingFields.push(field);
}
});
@@ -6076,7 +6154,7 @@ export const updateGroupRequest = async (data, isFromExtension) => {
];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
- if (data[field] !== undefined && data[field] !== null) {
+ if (data[field] === undefined || data[field] === null) {
missingFields.push(field);
}
});
@@ -6250,7 +6328,7 @@ export const sellNameRequest = async (data, isFromExtension) => {
const requiredFields = ['salePrice', 'nameForSale'];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
- if (data[field] !== undefined && data[field] !== null) {
+ if (data[field] === undefined || data[field] === null) {
missingFields.push(field);
}
});
@@ -6320,7 +6398,7 @@ export const cancelSellNameRequest = async (data, isFromExtension) => {
const requiredFields = ['nameForSale'];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
- if (data[field] !== undefined && data[field] !== null) {
+ if (data[field] === undefined || data[field] === null) {
missingFields.push(field);
}
});
@@ -6377,7 +6455,7 @@ export const buyNameRequest = async (data, isFromExtension) => {
const requiredFields = ['nameForSale'];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
- if (data[field] !== undefined && data[field] !== null) {
+ if (data[field] === undefined || data[field] === null) {
missingFields.push(field);
}
});
@@ -6908,12 +6986,11 @@ export const multiPaymentWithPrivateData = async (data, isFromExtension) => {
[
{
registeredName: encodeURIComponent(name),
- file: encryptDataResponse,
+ data: encryptDataResponse,
service: transaction.service,
identifier: encodeURIComponent(transaction.identifier),
- uploadType: 'file',
+ uploadType: 'base64',
description: transaction?.description,
- isBase64: true,
apiVersion: 2,
withFee: true,
},
diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts
deleted file mode 100644
index 6819842..0000000
--- a/src/qortalRequests/get.ts
+++ /dev/null
@@ -1,7077 +0,0 @@
-import { Sha256 } from 'asmcrypto.js';
-import {
- createEndpoint,
- getBalanceInfo,
- getFee,
- getKeyPair,
- getLastRef,
- getSaveWallet,
- processTransactionVersion2,
- signChatFunc,
- joinGroup as joinGroupFunc,
- sendQortFee,
- sendCoin as sendCoinFunc,
- createBuyOrderTx,
- performPowTask,
- parseErrorResponse,
- groupSecretkeys,
- registerName,
- updateName,
- leaveGroup,
- inviteToGroup,
- getNameInfoForOthers,
- kickFromGroup,
- banFromGroup,
- cancelBan,
- makeAdmin,
- removeAdmin,
- cancelInvitationToGroup,
- createGroup,
- updateGroup,
- sellName,
- cancelSellName,
- buyName,
- getBaseApi,
- getAssetBalanceInfo,
- getNameOrAddress,
- getAssetInfo,
- getPublicKey,
- transferAsset,
-} from '../background/background.ts';
-import { getNameInfo, uint8ArrayToObject } from '../encryption/encryption.ts';
-import { showSaveFilePicker } from '../hooks/useQortalMessageListener.tsx';
-import { getPublishesFromAdminsAdminSpace } from '../components/Chat/AdminSpaceInner.tsx';
-import { extractComponents } from '../components/Chat/MessageDisplay.tsx';
-import {
- decryptResource,
- getGroupAdmins,
- getPublishesFromAdmins,
- validateSecretKey,
-} from '../components/Group/Group.tsx';
-import { QORT_DECIMALS } from '../constants/constants.ts';
-import Base58 from '../encryption/Base58.ts';
-import ed2curve from '../encryption/ed2curve.ts';
-import nacl from '../encryption/nacl-fast.ts';
-import {
- base64ToUint8Array,
- createSymmetricKeyAndNonce,
- decryptDeprecatedSingle,
- decryptGroupDataQortalRequest,
- decryptGroupEncryptionWithSharingKey,
- decryptSingle,
- encryptDataGroup,
- encryptSingle,
- objectToBase64,
- uint8ArrayStartsWith,
- uint8ArrayToBase64,
-} from '../qdn/encryption/group-encryption.ts';
-import { publishData } from '../qdn/publish/publish.ts';
-import {
- getPermission,
- isRunningGateway,
- setPermission,
-} from './qortalRequests.ts';
-import TradeBotCreateRequest from '../transactions/TradeBotCreateRequest.ts';
-import DeleteTradeOffer from '../transactions/TradeBotDeleteRequest.ts';
-import signTradeBotTransaction from '../transactions/signTradeBotTransaction.ts';
-import { createTransaction } from '../transactions/transactions.ts';
-import { executeEvent } from '../utils/events.ts';
-import { fileToBase64 } from '../utils/fileReading/index.ts';
-import { mimeToExtensionMap } from '../utils/memeTypes.ts';
-import { RequestQueueWithPromise } from '../utils/queue/queue.ts';
-import utils from '../utils/utils.ts';
-import ShortUniqueId from 'short-unique-id';
-import { isValidBase64WithDecode } from '../utils/decode.ts';
-import i18n from 'i18next';
-
-const uid = new ShortUniqueId({ length: 6 });
-
-export const requestQueueGetAtAddresses = new RequestQueueWithPromise(10);
-
-const sellerForeignFee = {
- LITECOIN: {
- value: '~0.00005',
- ticker: 'LTC',
- },
- DOGECOIN: {
- value: '~0.005',
- ticker: 'DOGE',
- },
- BITCOIN: {
- value: '~0.0001',
- ticker: 'BTC',
- },
- DIGIBYTE: {
- value: '~0.0005',
- ticker: 'DGB',
- },
- RAVENCOIN: {
- value: '~0.006',
- ticker: 'RVN',
- },
- PIRATECHAIN: {
- value: '~0.0002',
- ticker: 'ARRR',
- },
-};
-
-const btcFeePerByte = 0.000001;
-const ltcFeePerByte = 0.0000003;
-const dogeFeePerByte = 0.00001;
-const dgbFeePerByte = 0.0000001;
-const rvnFeePerByte = 0.00001125;
-
-const MAX_RETRIES = 3; // Set max number of retries
-
-export async function retryTransaction(
- fn,
- args,
- throwError,
- retries = MAX_RETRIES
-) {
- let attempt = 0;
- while (attempt < retries) {
- try {
- return await fn(...args);
- } catch (error) {
- console.error(`Attempt ${attempt + 1} failed: ${error.message}`);
- attempt++;
- if (attempt === retries) {
- console.error(
- i18n.t('question:message.generic.max_retry_transaction', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- if (throwError) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.process_transaction', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- } else {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.process_transaction', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- }
- await new Promise((res) => setTimeout(res, 10000));
- }
- }
-}
-
-function roundUpToDecimals(number, decimals = 8) {
- const factor = Math.pow(10, decimals); // Create a factor based on the number of decimals
- return Math.ceil(+number * factor) / factor;
-}
-
-export const _createPoll = async (
- { pollName, pollDescription, options },
- isFromExtension,
- skipPermission
-) => {
- const fee = await getFee('CREATE_POLL');
- let resPermission = {};
- if (!skipPermission) {
- resPermission = await getUserPermission(
- {
- text1: i18n.t('question:request_create_poll', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:poll', {
- name: pollName,
- postProcess: 'capitalizeFirstChar',
- }),
- text3: i18n.t('question:description', {
- description: pollDescription,
- postProcess: 'capitalizeFirstChar',
- }),
- text4: i18n.t('question:options', {
- optionList: options?.join(', '),
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- }
-
- const { accepted = false } = resPermission;
-
- if (accepted || skipPermission) {
- const wallet = await getSaveWallet();
- const address = wallet.address0;
- 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,
- };
- let lastRef = await getLastRef();
-
- const tx = await createTransaction(8, keyPair, {
- fee: fee.fee,
- ownerAddress: address,
- rPollName: pollName,
- rPollDesc: pollDescription,
- rOptions: options,
- lastReference: lastRef,
- });
- const signedBytes = Base58.encode(tx.signedBytes);
- const res = await processTransactionVersion2(signedBytes);
- if (!res?.signature)
- throw new Error(
- res?.message ||
- i18n.t('question:message.error.process_transaction', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-const _deployAt = async (
- { name, description, tags, creationBytes, amount, assetId, atType },
- isFromExtension
-) => {
- const fee = await getFee('DEPLOY_AT');
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:deploy_at', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:name', {
- name: name,
- postProcess: 'capitalizeFirstChar',
- }),
- text3: i18n.t('question:description', {
- description: description,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
-
- const { accepted } = resPermission;
-
- if (accepted) {
- const wallet = await getSaveWallet();
- const address = wallet.address0;
- const lastReference = await getLastRef();
- 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(16, keyPair, {
- fee: fee.fee,
- rName: name,
- rDescription: description,
- rTags: tags,
- rAmount: amount,
- rAssetId: assetId,
- rCreationBytes: creationBytes,
- atType: atType,
- lastReference: lastReference,
- });
-
- const signedBytes = Base58.encode(tx.signedBytes);
-
- const res = await processTransactionVersion2(signedBytes);
- if (!res?.signature)
- throw new Error(
- res?.message ||
- i18n.t('question:message.error.process_transaction', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const _voteOnPoll = async (
- { pollName, optionIndex, optionName },
- isFromExtension,
- skipPermission
-) => {
- const fee = await getFee('VOTE_ON_POLL');
- let resPermission = {};
-
- if (!skipPermission) {
- resPermission = await getUserPermission(
- {
- text1: i18n.t('question:request_vote_poll', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:poll', {
- name: pollName,
- postProcess: 'capitalizeFirstChar',
- }),
- text3: i18n.t('question:option', {
- option: optionName,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- }
-
- const { accepted = false } = resPermission;
-
- if (accepted || skipPermission) {
- const wallet = await getSaveWallet();
- const address = wallet.address0;
- 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,
- };
- let lastRef = await getLastRef();
-
- const tx = await createTransaction(9, keyPair, {
- fee: fee.fee,
- voterAddress: address,
- rPollName: pollName,
- rOptionIndex: optionIndex,
- lastReference: lastRef,
- });
- const signedBytes = Base58.encode(tx.signedBytes);
- const res = await processTransactionVersion2(signedBytes);
- if (!res?.signature)
- throw new Error(
- res?.message ||
- i18n.t('question:message.error.process_transaction', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-// Map to store resolvers and rejectors by requestId
-const fileRequestResolvers = new Map();
-
-const handleFileMessage = (event) => {
- const { action, requestId, result, error } = event.data;
-
- if (
- action === 'getFileFromIndexedDBResponse' &&
- fileRequestResolvers.has(requestId)
- ) {
- const { resolve, reject } = fileRequestResolvers.get(requestId);
- fileRequestResolvers.delete(requestId); // Clean up after resolving
-
- if (result) {
- resolve(result);
- } else {
- reject(
- error ||
- i18n.t('question:message.error.retrieve_file', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- }
-};
-
-window.addEventListener('message', handleFileMessage);
-
-function getFileFromContentScript(fileId) {
- return new Promise((resolve, reject) => {
- const requestId = `getFile_${fileId}_${Date.now()}`;
-
- fileRequestResolvers.set(requestId, { resolve, reject }); // Store resolvers by requestId
- const targetOrigin = window.location.origin;
-
- // Send the request message
- window.postMessage(
- { action: 'getFileFromIndexedDB', fileId, requestId },
- targetOrigin
- );
-
- // Timeout to handle no response scenario
- setTimeout(() => {
- if (fileRequestResolvers.has(requestId)) {
- fileRequestResolvers.get(requestId).reject(
- i18n.t('question:message.error.timeout_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- fileRequestResolvers.delete(requestId); // Clean up on timeout
- }
- }, 10000); // 10-second timeout
- });
-}
-
-const responseResolvers = new Map();
-
-const handleMessage = (event) => {
- const { action, requestId, result } = event.data;
-
- // Check if this is the expected response action and if we have a stored resolver
- if (
- action === 'QORTAL_REQUEST_PERMISSION_RESPONSE' &&
- responseResolvers.has(requestId)
- ) {
- // Resolve the stored promise with the result
- responseResolvers.get(requestId)(result || false);
- responseResolvers.delete(requestId); // Clean up after resolving
- }
-};
-
-window.addEventListener('message', handleMessage);
-
-async function getUserPermission(payload, isFromExtension) {
- return new Promise((resolve) => {
- const requestId = `qortalRequest_${Date.now()}`;
- responseResolvers.set(requestId, resolve); // Store resolver by requestId
- const targetOrigin = window.location.origin;
-
- // Send the request message
- window.postMessage(
- {
- action: 'QORTAL_REQUEST_PERMISSION',
- payload,
- requestId,
- isFromExtension,
- },
- targetOrigin
- );
-
- // Optional timeout to handle no response scenario
- setTimeout(() => {
- if (responseResolvers.has(requestId)) {
- responseResolvers.get(requestId)(false); // Resolve with `false` if no response
- responseResolvers.delete(requestId);
- }
- }, 60000); // 30-second timeout
- });
-}
-
-export const getUserAccount = async ({
- isFromExtension,
- appInfo,
- skipAuth,
-}) => {
- try {
- const value =
- (await getPermission(`qAPPAutoAuth-${appInfo?.name}`)) || false;
- let skip = false;
- if (value) {
- skip = true;
- }
- if (skipAuth) {
- skip = true;
- }
- let resPermission;
- if (!skip) {
- resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.authenticate', {
- postProcess: 'capitalizeFirstChar',
- }),
- checkbox1: {
- value: false,
- label: i18n.t('question:always_authenticate', {
- postProcess: 'capitalizeFirstChar',
- }),
- },
- },
- isFromExtension
- );
- }
-
- const { accepted = false, checkbox1 = false } = resPermission || {};
- if (resPermission) {
- setPermission(`qAPPAutoAuth-${appInfo?.name}`, checkbox1);
- }
- if (accepted || skip) {
- const wallet = await getSaveWallet();
- const address = wallet.address0;
- const publicKey = wallet.publicKey;
- return {
- address,
- publicKey,
- };
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } catch (error) {
- throw new Error(
- i18n.t('auth:message.error.fetch_user_account', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const encryptData = async (data, sender) => {
- let data64 = data.data64 || data.base64;
- let publicKeys = data.publicKeys || [];
- if (data?.file || data?.blob) {
- data64 = await fileToBase64(data?.file || data?.blob);
- }
- if (!data64) {
- throw new Error(
- i18n.t('question:message.generic.include_data_encrypt', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- const privateKey = parsedData.privateKey;
- const userPublicKey = parsedData.publicKey;
-
- const encryptDataResponse = encryptDataGroup({
- data64,
- publicKeys: publicKeys,
- privateKey,
- userPublicKey,
- });
- if (encryptDataResponse) {
- return encryptDataResponse;
- } else {
- throw new Error(
- i18n.t('question:message.error.encrypt', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const encryptQortalGroupData = async (data, sender) => {
- let data64 = data?.data64 || data?.base64;
- let groupId = data?.groupId;
- let isAdmins = data?.isAdmins;
- if (!groupId) {
- throw new Error(
- i18n.t('question:message.generic.provide_group_id', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- if (data?.file || data?.blob) {
- data64 = await fileToBase64(data?.file || data?.blob);
- }
- if (!data64) {
- throw new Error(
- i18n.t('question:message.generic.include_data_encrypt', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- let secretKeyObject;
- if (!isAdmins) {
- if (
- groupSecretkeys[groupId] &&
- groupSecretkeys[groupId].secretKeyObject &&
- groupSecretkeys[groupId]?.timestamp &&
- Date.now() - groupSecretkeys[groupId]?.timestamp < 1200000
- ) {
- secretKeyObject = groupSecretkeys[groupId].secretKeyObject;
- }
-
- if (!secretKeyObject) {
- const { names } = await getGroupAdmins(groupId);
-
- const publish = await getPublishesFromAdmins(names, groupId);
- if (publish === false)
- throw new Error(
- i18n.t('question:message.error.no_group_key', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const url = await createEndpoint(
- `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${
- publish.identifier
- }?encoding=base64&rebuild=true`
- );
-
- const res = await fetch(url);
- const resData = await res.text();
-
- const decryptedKey: any = await decryptResource(resData, true);
-
- const dataint8Array = base64ToUint8Array(decryptedKey.data);
- const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
-
- if (!validateSecretKey(decryptedKeyToObject))
- throw new Error(
- i18n.t('auth:message.error.invalid_secret_key', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- secretKeyObject = decryptedKeyToObject;
- groupSecretkeys[groupId] = {
- secretKeyObject,
- timestamp: Date.now(),
- };
- }
- } else {
- if (
- groupSecretkeys[`admins-${groupId}`] &&
- groupSecretkeys[`admins-${groupId}`].secretKeyObject &&
- groupSecretkeys[`admins-${groupId}`]?.timestamp &&
- Date.now() - groupSecretkeys[`admins-${groupId}`]?.timestamp < 1200000
- ) {
- secretKeyObject = groupSecretkeys[`admins-${groupId}`].secretKeyObject;
- }
-
- if (!secretKeyObject) {
- const { names } = await getGroupAdmins(groupId);
-
- const publish = await getPublishesFromAdminsAdminSpace(names, groupId);
- if (publish === false)
- throw new Error(
- i18n.t('question:message.error.no_group_key', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const url = await createEndpoint(
- `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${
- publish.identifier
- }?encoding=base64&rebuild=true`
- );
-
- const res = await fetch(url);
- const resData = await res.text();
- const decryptedKey: any = await decryptResource(resData, true);
- const dataint8Array = base64ToUint8Array(decryptedKey.data);
- const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
-
- if (!validateSecretKey(decryptedKeyToObject))
- throw new Error(
- i18n.t('auth:message.error.invalid_secret_key', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- secretKeyObject = decryptedKeyToObject;
- groupSecretkeys[`admins-${groupId}`] = {
- secretKeyObject,
- timestamp: Date.now(),
- };
- }
- }
-
- const resGroupEncryptedResource = encryptSingle({
- data64,
- secretKeyObject: secretKeyObject,
- });
-
- if (resGroupEncryptedResource) {
- return resGroupEncryptedResource;
- } else {
- throw new Error(
- i18n.t('question:message.error.encrypt', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const decryptQortalGroupData = async (data, sender) => {
- let data64 = data?.data64 || data?.base64;
- let groupId = data?.groupId;
- let isAdmins = data?.isAdmins;
- if (!groupId) {
- throw new Error(
- i18n.t('question:message.generic.provide_group_id', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- if (!data64) {
- throw new Error(
- i18n.t('question:message.generic.include_data_encrypt', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- let secretKeyObject;
- if (!isAdmins) {
- if (
- groupSecretkeys[groupId] &&
- groupSecretkeys[groupId].secretKeyObject &&
- groupSecretkeys[groupId]?.timestamp &&
- Date.now() - groupSecretkeys[groupId]?.timestamp < 1200000
- ) {
- secretKeyObject = groupSecretkeys[groupId].secretKeyObject;
- }
- if (!secretKeyObject) {
- const { names } = await getGroupAdmins(groupId);
-
- const publish = await getPublishesFromAdmins(names, groupId);
- if (publish === false)
- throw new Error(
- i18n.t('question:message.error.no_group_key', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const url = await createEndpoint(
- `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${
- publish.identifier
- }?encoding=base64&rebuild=true`
- );
-
- const res = await fetch(url);
- const resData = await res.text();
- const decryptedKey: any = await decryptResource(resData, true);
-
- const dataint8Array = base64ToUint8Array(decryptedKey.data);
- const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
- if (!validateSecretKey(decryptedKeyToObject))
- throw new Error(
- i18n.t('auth:message.error.invalid_secret_key', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- secretKeyObject = decryptedKeyToObject;
- groupSecretkeys[groupId] = {
- secretKeyObject,
- timestamp: Date.now(),
- };
- }
- } else {
- if (
- groupSecretkeys[`admins-${groupId}`] &&
- groupSecretkeys[`admins-${groupId}`].secretKeyObject &&
- groupSecretkeys[`admins-${groupId}`]?.timestamp &&
- Date.now() - groupSecretkeys[`admins-${groupId}`]?.timestamp < 1200000
- ) {
- secretKeyObject = groupSecretkeys[`admins-${groupId}`].secretKeyObject;
- }
- if (!secretKeyObject) {
- const { names } = await getGroupAdmins(groupId);
-
- const publish = await getPublishesFromAdminsAdminSpace(names, groupId);
- if (publish === false)
- throw new Error(
- i18n.t('question:message.error.no_group_key', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const url = await createEndpoint(
- `/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${
- publish.identifier
- }?encoding=base64&rebuild=true`
- );
-
- const res = await fetch(url);
- const resData = await res.text();
- const decryptedKey: any = await decryptResource(resData, true);
-
- const dataint8Array = base64ToUint8Array(decryptedKey.data);
- const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
- if (!validateSecretKey(decryptedKeyToObject))
- throw new Error(
- i18n.t('auth:message.error.invalid_secret_key', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- secretKeyObject = decryptedKeyToObject;
- groupSecretkeys[`admins-${groupId}`] = {
- secretKeyObject,
- timestamp: Date.now(),
- };
- }
- }
-
- const resGroupDecryptResource = decryptSingle({
- data64,
- secretKeyObject: secretKeyObject,
- skipDecodeBase64: true,
- });
- if (resGroupDecryptResource) {
- return resGroupDecryptResource;
- } else {
- throw new Error(
- i18n.t('question:message.error.encrypt', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const encryptDataWithSharingKey = async (data, sender) => {
- let data64 = data?.data64 || data?.base64;
- let publicKeys = data.publicKeys || [];
- if (data?.file || data?.blob) {
- data64 = await fileToBase64(data?.file || data?.blob);
- }
- if (!data64) {
- throw new Error(
- i18n.t('question:message.generic.include_data_encrypt', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const symmetricKey = createSymmetricKeyAndNonce();
- const dataObject = {
- data: data64,
- key: symmetricKey.messageKey,
- };
- const dataObjectBase64 = await objectToBase64(dataObject);
-
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- const privateKey = parsedData.privateKey;
- const userPublicKey = parsedData.publicKey;
-
- const encryptDataResponse = encryptDataGroup({
- data64: dataObjectBase64,
- publicKeys: publicKeys,
- privateKey,
- userPublicKey,
- customSymmetricKey: symmetricKey.messageKey,
- });
- if (encryptDataResponse) {
- return encryptDataResponse;
- } else {
- throw new Error(
- i18n.t('question:message.error.encrypt', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const decryptDataWithSharingKey = async (data, sender) => {
- const { encryptedData, key } = data;
-
- if (!encryptedData) {
- throw new Error(
- i18n.t('question:message.generic.include_data_decrypt', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- const decryptedData = await decryptGroupEncryptionWithSharingKey({
- data64EncryptedData: encryptedData,
- key,
- });
- const base64ToObject = JSON.parse(atob(decryptedData));
-
- if (!base64ToObject.data)
- throw new Error(
- i18n.t('question:message.error.no_data_encrypted_resource', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- return base64ToObject.data;
-};
-
-export const getHostedData = async (data, isFromExtension) => {
- const isGateway = await isRunningGateway();
- if (isGateway) {
- throw new Error(
- i18n.t('question:message.generic.no_action_public_node', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:message.error.submit_sell_order', {
- postProcess: 'capitalizeFirstChar',
- }),
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const limit = data?.limit ? data?.limit : 20;
- const query = data?.query ? data?.query : '';
- const offset = data?.offset ? data?.offset : 0;
-
- let urlPath = `/arbitrary/hosted/resources/?limit=${limit}&offset=${offset}`;
- if (query) {
- urlPath = urlPath + `&query=${query}`;
- }
-
- const url = await createEndpoint(urlPath);
- const response = await fetch(url);
- const dataResponse = await response.json();
- return dataResponse;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_list', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const deleteHostedData = async (data, isFromExtension) => {
- const isGateway = await isRunningGateway();
- if (isGateway) {
- throw new Error(
- i18n.t('question:message.generic.no_action_public_node', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const requiredFields = ['hostedData'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.delete_hosts_resources', {
- size: data?.hostedData?.length,
- postProcess: 'capitalizeFirstChar',
- }),
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const { hostedData } = data;
-
- for (const hostedDataItem of hostedData) {
- try {
- const url = await createEndpoint(
- `/arbitrary/resource/${hostedDataItem.service}/${hostedDataItem.name}/${hostedDataItem.identifier}`
- );
- await fetch(url, {
- method: 'DELETE',
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- } catch (error) {
- console.log(error);
- }
- }
-
- return true;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_delete_hosted_resources', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-export const decryptData = async (data) => {
- const { encryptedData, publicKey } = data;
-
- if (!encryptedData) {
- throw new Error(`Missing fields: encryptedData`);
- }
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- const uint8PrivateKey = Base58.decode(parsedData.privateKey);
- const uint8Array = base64ToUint8Array(encryptedData);
- const startsWithQortalEncryptedData = uint8ArrayStartsWith(
- uint8Array,
- 'qortalEncryptedData'
- );
- if (startsWithQortalEncryptedData) {
- if (!publicKey) {
- throw new Error(`Missing fields: publicKey`);
- }
-
- const decryptedDataToBase64 = decryptDeprecatedSingle(
- uint8Array,
- publicKey,
- uint8PrivateKey
- );
- return decryptedDataToBase64;
- }
- const startsWithQortalGroupEncryptedData = uint8ArrayStartsWith(
- uint8Array,
- 'qortalGroupEncryptedData'
- );
- if (startsWithQortalGroupEncryptedData) {
- const decryptedData = decryptGroupDataQortalRequest(
- encryptedData,
- parsedData.privateKey
- );
- const decryptedDataToBase64 = uint8ArrayToBase64(decryptedData);
- return decryptedDataToBase64;
- }
- throw new Error(
- i18n.t('question:message.error.encrypt', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-};
-
-export const getListItems = async (data, isFromExtension) => {
- const isGateway = await isRunningGateway();
- if (isGateway) {
- throw new Error(
- i18n.t('question:message.generic.no_action_public_node', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const requiredFields = ['list_name'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const value = (await getPermission('qAPPAutoLists')) || false;
-
- let skip = false;
- if (value) {
- skip = true;
- }
- let resPermission;
- let acceptedVar;
- let checkbox1Var;
- if (!skip) {
- resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.access_list', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: data.list_name,
- checkbox1: {
- value: value,
- label: i18n.t('question:always_retrieve_list', {
- postProcess: 'capitalizeFirstChar',
- }),
- },
- },
- isFromExtension
- );
- const { accepted, checkbox1 } = resPermission;
- acceptedVar = accepted;
- checkbox1Var = checkbox1;
- setPermission('qAPPAutoLists', checkbox1);
- }
-
- if (acceptedVar || skip) {
- const url = await createEndpoint(`/lists/${data.list_name}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_list', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- const list = await response.json();
- return list;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_share_list', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const addListItems = async (data, isFromExtension) => {
- const isGateway = await isRunningGateway();
- if (isGateway) {
- throw new Error(
- i18n.t('question:message.generic.no_action_public_node', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const requiredFields = ['list_name', 'items'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const items = data.items;
- const list_name = data.list_name;
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.all_item_list', {
- name: list_name,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: items.join(', '),
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const url = await createEndpoint(`/lists/${list_name}`);
- const body = {
- items: items,
- };
- const bodyToString = JSON.stringify(body);
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: bodyToString,
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.add_to_list', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_add_list', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const deleteListItems = async (data, isFromExtension) => {
- const isGateway = await isRunningGateway();
- if (isGateway) {
- throw new Error(
- i18n.t('question:message.generic.no_action_public_node', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const requiredFields = ['list_name'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- if (!data?.item && !data?.items) {
- throw new Error(
- i18n.t('question:message.error.missing_fields', {
- fields: 'items',
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const item = data?.item;
- const items = data?.items;
- const list_name = data.list_name;
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.remove_from_list', {
- name: list_name,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: items ? JSON.stringify(items) : item,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const url = await createEndpoint(`/lists/${list_name}`);
- const body = {
- items: items || [item],
- };
- const bodyToString = JSON.stringify(body);
- const response = await fetch(url, {
- method: 'DELETE',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: bodyToString,
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.add_to_list', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_delete_from_list', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const publishQDNResource = async (
- data: any,
- sender,
- isFromExtension
-) => {
- const requiredFields = ['service'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- if (!data.file && !data.data64 && !data.base64) {
- throw new Error(
- i18n.t('question:message.error.no_data_file_submitted', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- // Use "default" if user hasn't specified an identifier
- const service = data.service;
- const appFee = data?.appFee ? +data.appFee : undefined;
- const appFeeRecipient = data?.appFeeRecipient;
- let hasAppFee = false;
- if (appFee && appFee > 0 && appFeeRecipient) {
- hasAppFee = true;
- }
-
- const registeredName = data?.name || (await getNameInfo());
- const name = registeredName;
- if (!name) {
- throw new Error(
- i18n.t('question:message.error.user_qortal_name', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- let identifier = data.identifier;
- let data64 = data.data64 || data.base64;
- const filename = data.filename;
- const title = data.title;
- const description = data.description;
- const category = data.category;
- const file = data?.file || data?.blob;
- const tags = data?.tags || [];
- const result = {};
-
- // Fill tags dynamically while maintaining backward compatibility
- for (let i = 0; i < 5; i++) {
- result[`tag${i + 1}`] = tags[i] || data[`tag${i + 1}`] || undefined;
- }
-
- // Access tag1 to tag5 from result
- const { tag1, tag2, tag3, tag4, tag5 } = result;
-
- if (data.identifier == null) {
- identifier = 'default';
- }
-
- if (
- data.encrypt &&
- (!data.publicKeys ||
- (Array.isArray(data.publicKeys) && data.publicKeys.length === 0))
- ) {
- throw new Error(
- i18n.t('question:message.error.encryption_requires_public_key', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- if (data.encrypt) {
- try {
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- const privateKey = parsedData.privateKey;
- const userPublicKey = parsedData.publicKey;
- if (data?.file || data?.blob) {
- data64 = await fileToBase64(data?.file || data?.blob);
- }
- const encryptDataResponse = encryptDataGroup({
- data64,
- publicKeys: data.publicKeys,
- privateKey,
- userPublicKey,
- });
- if (encryptDataResponse) {
- data64 = encryptDataResponse;
- }
- } catch (error) {
- throw new Error(
- error.message ||
- i18n.t('question:message.error.upload_encryption', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- }
-
- const fee = await getFee('ARBITRARY');
-
- const handleDynamicValues = {};
- if (hasAppFee) {
- const feePayment = await getFee('PAYMENT');
-
- (handleDynamicValues['appFee'] = +appFee + +feePayment.fee),
- (handleDynamicValues['checkbox1'] = {
- value: true,
- label: i18n.t('question:accept_app_fee', {
- postProcess: 'capitalizeFirstChar',
- }),
- });
- }
- if (!!data?.encrypt) {
- handleDynamicValues['highlightedText'] = `isEncrypted: ${!!data.encrypt}`;
- }
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.publish_qdn', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: `service: ${service}`,
- text3: `identifier: ${identifier || null}`,
- text4: `name: ${registeredName}`,
- fee: fee.fee,
- ...handleDynamicValues,
- },
- isFromExtension
- );
- const { accepted, checkbox1 = false } = resPermission;
- if (accepted) {
- try {
- const resPublish = await publishData({
- registeredName: encodeURIComponent(name),
- data: data64 ? data64 : file,
- service: service,
- identifier: encodeURIComponent(identifier),
- uploadType: data64 ? 'base64' : 'file',
- filename: filename,
- title,
- description,
- category,
- tag1,
- tag2,
- tag3,
- tag4,
- tag5,
- apiVersion: 2,
- withFee: true,
- });
- if (resPublish?.signature && hasAppFee && checkbox1) {
- sendCoinFunc(
- {
- amount: appFee,
- receiver: appFeeRecipient,
- },
- true
- );
- }
- return resPublish;
- } catch (error) {
- throw new Error(error?.message || 'Upload failed');
- }
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const checkArrrSyncStatus = async (seed) => {
- const _url = await createEndpoint(`/crosschain/arrr/syncstatus`);
- let tries = 0; // Track the number of attempts
-
- while (tries < 36) {
- const response = await fetch(_url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: seed,
- });
-
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
-
- if (res.indexOf('<') > -1 || res !== 'Synchronized') {
- // Wait 2 seconds before trying again
- await new Promise((resolve) => setTimeout(resolve, 2000));
- tries += 1;
- } else {
- // If the response doesn't meet the two conditions, exit the function
- return;
- }
- }
-
- // If we exceed N tries, throw an error
- throw new Error(
- i18n.t('question:message.error.synchronization_attempts', {
- quantity: 36,
- postProcess: 'capitalizeFirstChar',
- })
- );
-};
-
-export const publishMultipleQDNResources = async (
- data: any,
- sender,
- isFromExtension
-) => {
- const requiredFields = ['resources'];
- const missingFields: string[] = [];
-
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const resources = data.resources;
- if (!Array.isArray(resources)) {
- throw new Error(
- i18n.t('group:message.generic.invalid_data', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- if (resources.length === 0) {
- throw new Error(
- i18n.t('question:message.error.no_resources_publish', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- const encrypt = data?.encrypt;
-
- for (const resource of resources) {
- const resourceEncrypt = encrypt && resource?.disableEncrypt !== true;
-
- if (!resourceEncrypt && resource?.service.endsWith('_PRIVATE')) {
- const errorMsg = i18n.t('question:message.error.only_encrypted_data', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- } else if (resourceEncrypt && !resource?.service.endsWith('_PRIVATE')) {
- const errorMsg = i18n.t('question:message.error.use_private_service', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- }
-
- const fee = await getFee('ARBITRARY');
- const registeredName = await getNameInfo();
-
- const name = registeredName;
-
- if (!name) {
- throw new Error(
- i18n.t('question:message.error.registered_name', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- const userNames = await getAllUserNames();
- data.resources?.forEach((item) => {
- if (item?.name && !userNames?.includes(item.name))
- throw new Error(
- `The name ${item.name}, does not belong to the publisher.`
- );
- });
-
- const appFee = data?.appFee ? +data.appFee : undefined;
- const appFeeRecipient = data?.appFeeRecipient;
- let hasAppFee = false;
-
- if (appFee && appFee > 0 && appFeeRecipient) {
- hasAppFee = true;
- }
-
- const handleDynamicValues = {};
- if (hasAppFee) {
- const feePayment = await getFee('PAYMENT');
-
- (handleDynamicValues['appFee'] = +appFee + +feePayment.fee),
- (handleDynamicValues['checkbox1'] = {
- value: true,
- label: i18n.t('question:accept_app_fee', {
- postProcess: 'capitalizeFirstChar',
- }),
- });
- }
- if (data?.encrypt) {
- handleDynamicValues['highlightedText'] = `isEncrypted: ${!!data.encrypt}`;
- }
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.publish_qdn', {
- postProcess: 'capitalizeFirstChar',
- }),
- html: `
-
-
-
- ${data.resources
- .map(
- (resource) => `
-
-
Service: ${
- resource.service
- }
-
Name: ${resource?.name || name}
-
Identifier: ${
- resource.identifier
- }
- ${
- resource.filename
- ? `
Filename: ${resource.filename}
`
- : ''
- }
-
`
- )
- .join('')}
-
-
- `,
- fee: +fee.fee * resources.length,
- ...handleDynamicValues,
- },
- isFromExtension
- );
-
- const { accepted, checkbox1 = false } = resPermission;
- if (!accepted) {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- type FailedPublish = {
- reason: string;
- identifier: any;
- service: any;
- };
-
- const failedPublishesIdentifiers: FailedPublish[] = [];
-
- for (const resource of resources) {
- try {
- const requiredFields = ['service'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!resource[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- failedPublishesIdentifiers.push({
- reason: errorMsg,
- identifier: resource.identifier,
- service: resource.service,
- name: resource?.name || name,
- });
- continue;
- }
- if (!resource.file && !resource.data64 && !resource?.base64) {
- const errorMsg = i18n.t(
- 'question:message.error.no_data_file_submitted',
- {
- postProcess: 'capitalizeFirstChar',
- }
- );
- failedPublishesIdentifiers.push({
- reason: errorMsg,
- identifier: resource.identifier,
- service: resource.service,
- name: resource?.name || name,
- });
- continue;
- }
- const service = resource.service;
- let identifier = resource.identifier;
- let rawData = resource?.data64 || resource?.base64;
- const filename = resource.filename;
- const title = resource.title;
- const description = resource.description;
- const category = resource.category;
- const tags = resource?.tags || [];
- const result = {};
- // Fill tags dynamically while maintaining backward compatibility
- for (let i = 0; i < 5; i++) {
- result[`tag${i + 1}`] = tags[i] || resource[`tag${i + 1}`] || undefined;
- }
-
- // Access tag1 to tag5 from result
- const { tag1, tag2, tag3, tag4, tag5 } = result;
- const resourceEncrypt = encrypt && resource?.disableEncrypt !== true;
- if (resource.identifier == null) {
- identifier = 'default';
- }
- if (!resourceEncrypt && service.endsWith('_PRIVATE')) {
- const errorMsg = i18n.t('question:message.error.only_encrypted_data', {
- postProcess: 'capitalizeFirstChar',
- });
- failedPublishesIdentifiers.push({
- reason: errorMsg,
- identifier: resource.identifier,
- service: resource.service,
- name: resource?.name || name,
- });
- continue;
- }
- if (resource.file) {
- rawData = resource.file;
- }
-
- if (resourceEncrypt) {
- try {
- if (resource?.file) {
- rawData = await fileToBase64(resource.file);
- }
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- const privateKey = parsedData.privateKey;
- const userPublicKey = parsedData.publicKey;
- const encryptDataResponse = encryptDataGroup({
- data64: rawData,
- publicKeys: data.publicKeys,
- privateKey,
- userPublicKey,
- });
- if (encryptDataResponse) {
- rawData = encryptDataResponse;
- }
- } catch (error) {
- const errorMsg =
- error?.message ||
- i18n.t('question:message.error.upload_encryption', {
- postProcess: 'capitalizeFirstChar',
- });
- failedPublishesIdentifiers.push({
- reason: errorMsg,
- identifier: resource.identifier,
- service: resource.service,
- name: resource?.name || name,
- });
- continue;
- }
- }
-
- try {
- const dataType =
- resource?.base64 || resource?.data64 || resourceEncrypt
- ? 'base64'
- : 'file';
- console.log('dataType', dataType);
- await retryTransaction(
- publishData,
- [
- {
- data: rawData,
- registeredName: encodeURIComponent(resource?.name || name),
- service: service,
- identifier: encodeURIComponent(identifier),
- uploadType: dataType,
- filename: filename,
- title,
- description,
- category,
- tag1,
- tag2,
- tag3,
- tag4,
- tag5,
- apiVersion: 2,
- withFee: true,
- },
- ],
- true
- );
- await new Promise((res) => {
- setTimeout(() => {
- res();
- }, 1000);
- });
- } catch (error) {
- const errorMsg =
- error.message ||
- i18n.t('question:message.error.upload', {
- postProcess: 'capitalizeFirstChar',
- });
- failedPublishesIdentifiers.push({
- reason: errorMsg,
- identifier: resource.identifier,
- service: resource.service,
- name: resource?.name || name,
- });
- }
- } catch (error) {
- failedPublishesIdentifiers.push({
- reason:
- error?.message ||
- i18n.t('question:message.error.unknown_error', {
- postProcess: 'capitalizeFirstChar',
- }),
- identifier: resource.identifier,
- service: resource.service,
- name: resource?.name || name,
- });
- }
- }
- if (failedPublishesIdentifiers.length > 0) {
- const obj = {
- message: i18n.t('question:message.error.resources_publish', {
- postProcess: 'capitalizeFirstChar',
- }),
- };
- obj['error'] = {
- unsuccessfulPublishes: failedPublishesIdentifiers,
- };
- return obj;
- }
- if (hasAppFee && checkbox1) {
- sendCoinFunc(
- {
- amount: appFee,
- receiver: appFeeRecipient,
- },
- true
- );
- }
- return true;
-};
-
-export const voteOnPoll = async (data, isFromExtension) => {
- const requiredFields = ['pollName', 'optionIndex'];
- const missingFields: string[] = [];
-
- requiredFields.forEach((field) => {
- if (!data[field] && data[field] !== 0) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const pollName = data.pollName;
- const optionIndex = data.optionIndex;
- let pollInfo = null;
- try {
- const url = await createEndpoint(`/polls/${encodeURIComponent(pollName)}`);
- const response = await fetch(url);
- if (!response.ok) {
- const errorMessage = await parseErrorResponse(
- response,
- i18n.t('question:message.error.fetch_poll', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- throw new Error(errorMessage);
- }
-
- pollInfo = await response.json();
- } catch (error) {
- const errorMsg =
- (error && error.message) ||
- i18n.t('question:message.error.no_poll', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- if (!pollInfo || pollInfo.error) {
- const errorMsg =
- (pollInfo && pollInfo.message) ||
- i18n.t('question:message.error.no_poll', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- try {
- const optionName = pollInfo.pollOptions[optionIndex].optionName;
- const resVoteOnPoll = await _voteOnPoll(
- { pollName, optionIndex, optionName },
- isFromExtension
- );
- return resVoteOnPoll;
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.poll_vote', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const createPoll = async (data, isFromExtension) => {
- const requiredFields = [
- 'pollName',
- 'pollDescription',
- 'pollOptions',
- 'pollOwnerAddress',
- ];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const pollName = data.pollName;
- const pollDescription = data.pollDescription;
- const pollOptions = data.pollOptions;
- try {
- const resCreatePoll = await _createPoll(
- {
- pollName,
- pollDescription,
- options: pollOptions,
- },
- isFromExtension
- );
- return resCreatePoll;
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.poll_create', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-function isBase64(str) {
- const base64Regex =
- /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
- return base64Regex.test(str) && str.length % 4 === 0;
-}
-
-function checkValue(value) {
- if (typeof value === 'string') {
- if (isBase64(value)) {
- return 'string';
- } else {
- return 'string';
- }
- } else if (typeof value === 'object' && value !== null) {
- return 'object';
- } else {
- throw new Error(
- i18n.t('question:message.error.invalid_fullcontent', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-}
-
-export const sendChatMessage = async (data, isFromExtension, appInfo) => {
- const message = data?.message;
- const fullMessageObject = data?.fullMessageObject || data?.fullContent;
- const recipient = data?.destinationAddress || data.recipient;
- const groupId = data.groupId;
- const isRecipient = groupId === undefined;
- const chatReference = data?.chatReference;
- if (groupId === undefined && recipient === undefined) {
- throw new Error(
- i18n.t('question:provide_recipient_group_id', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- let fullMessageObjectType;
- if (fullMessageObject) {
- fullMessageObjectType = checkValue(fullMessageObject);
- }
- const value =
- (await getPermission(`qAPPSendChatMessage-${appInfo?.name}`)) || false;
- let skip = false;
- if (value) {
- skip = true;
- }
- let resPermission;
- if (!skip) {
- resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.send_chat_message', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: isRecipient
- ? i18n.t('question:to_recipient', {
- recipient: recipient,
- postProcess: 'capitalizeFirstChar',
- })
- : i18n.t('question:to_group', {
- group_id: groupId,
- postProcess: 'capitalizeFirstChar',
- }),
- text3: fullMessageObject
- ? fullMessageObjectType === 'string'
- ? `${fullMessageObject?.slice(0, 25)}${fullMessageObject?.length > 25 ? '...' : ''}`
- : `${JSON.stringify(fullMessageObject)?.slice(0, 25)}${JSON.stringify(fullMessageObject)?.length > 25 ? '...' : ''}`
- : `${message?.slice(0, 25)}${message?.length > 25 ? '...' : ''}`,
- checkbox1: {
- value: false,
- label: i18n.t('question:always_chat_messages', {
- postProcess: 'capitalizeFirstChar',
- }),
- },
- },
- isFromExtension
- );
- }
- const { accepted = false, checkbox1 = false } = resPermission || {};
- if (resPermission && accepted) {
- setPermission(`qAPPSendChatMessage-${appInfo?.name}`, checkbox1);
- }
- if (accepted || skip) {
- const tiptapJson = {
- type: 'doc',
- content: [
- {
- type: 'paragraph',
- content: [
- {
- type: 'text',
- text: message,
- },
- ],
- },
- ],
- };
- const messageObject = fullMessageObject
- ? fullMessageObject
- : {
- messageText: tiptapJson,
- images: [],
- repliedTo: '',
- version: 3,
- };
-
- let stringifyMessageObject = JSON.stringify(messageObject);
- if (fullMessageObjectType === 'string') {
- stringifyMessageObject = messageObject;
- }
-
- const balance = await getBalanceInfo();
- const hasEnoughBalance = +balance < 4 ? false : true;
- if (!hasEnoughBalance) {
- throw new Error(
- i18n.t('group:message.error.qortals_required', {
- quantity: 4,
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- if (isRecipient && recipient) {
- const url = await createEndpoint(`/addresses/publickey/${recipient}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_recipient_public_key', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- let key;
- let hasPublicKey;
- let res;
- const contentType = response.headers.get('content-type');
-
- // If the response is JSON, parse it as JSON
- if (contentType && contentType.includes('application/json')) {
- res = await response.json();
- } else {
- // Otherwise, treat it as plain text
- res = await response.text();
- }
- if (res?.error === 102) {
- key = '';
- hasPublicKey = false;
- } else if (res !== false) {
- key = res;
- hasPublicKey = true;
- } else {
- key = '';
- hasPublicKey = false;
- }
-
- if (!hasPublicKey && isRecipient) {
- throw new Error(
- 'Cannot send an encrypted message to this user since they do not have their publickey on chain.'
- );
- }
- let _reference = new Uint8Array(64);
- self.crypto.getRandomValues(_reference);
-
- let sendTimestamp = Date.now();
-
- let reference = Base58.encode(_reference);
- 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,
- };
-
- let handleDynamicValues = {};
- if (chatReference) {
- handleDynamicValues['chatReference'] = chatReference;
- }
-
- const tx = await createTransaction(18, keyPair, {
- timestamp: sendTimestamp,
- recipient: recipient,
- recipientPublicKey: key,
- hasChatReference: chatReference ? 1 : 0,
- message: stringifyMessageObject,
- lastReference: reference,
- proofOfWorkNonce: 0,
- isEncrypted: 1,
- isText: 1,
- ...handleDynamicValues,
- });
-
- const chatBytes = tx.chatBytes;
- const difficulty = 8;
- const { nonce, chatBytesArray } = await performPowTask(
- chatBytes,
- difficulty
- );
-
- let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair);
- if (_response?.error) {
- throw new Error(_response?.message);
- }
- return _response;
- } else if (!isRecipient && groupId) {
- let _reference = new Uint8Array(64);
- self.crypto.getRandomValues(_reference);
-
- let reference = Base58.encode(_reference);
- 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,
- };
-
- let handleDynamicValues = {};
- if (chatReference) {
- handleDynamicValues['chatReference'] = chatReference;
- }
-
- const txBody = {
- timestamp: Date.now(),
- groupID: Number(groupId),
- hasReceipient: 0,
- hasChatReference: chatReference ? 1 : 0,
- message: stringifyMessageObject,
- lastReference: reference,
- proofOfWorkNonce: 0,
- isEncrypted: 0, // Set default to not encrypted for groups
- isText: 1,
- ...handleDynamicValues,
- };
-
- const tx = await createTransaction(181, keyPair, txBody);
-
- // if (!hasEnoughBalance) {
- // throw new Error("Must have at least 4 QORT to send a chat message");
- // }
-
- const chatBytes = tx.chatBytes;
- const difficulty = 8;
- const { nonce, chatBytesArray } = await performPowTask(
- chatBytes,
- difficulty
- );
-
- let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair);
- if (_response?.error) {
- throw new Error(_response?.message);
- }
- return _response;
- } else {
- throw new Error(
- i18n.t('question:provide_recipient_group_id', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_send_message', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const joinGroup = async (data, isFromExtension) => {
- const requiredFields = ['groupId'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- let groupInfo = null;
- try {
- const url = await createEndpoint(`/groups/${data.groupId}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_group', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- groupInfo = await response.json();
- } catch (error) {
- const errorMsg =
- (error && error.message) ||
- i18n.t('question:message.error.no_group_found', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const fee = await getFee('JOIN_GROUP');
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:message.generic.confirm_join_group', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: `${groupInfo.groupName}`,
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const groupId = data.groupId;
-
- if (!groupInfo || groupInfo.error) {
- const errorMsg =
- (groupInfo && groupInfo.message) ||
- i18n.t('question:message.error.no_group_found', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- try {
- const resJoinGroup = await joinGroupFunc({ groupId });
- return resJoinGroup;
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('group:message.error.group_join', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_join', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const saveFile = async (data, sender, isFromExtension, snackMethods) => {
- try {
- if (!data?.filename) throw new Error('Missing filename');
- if (data?.location) {
- const requiredFieldsLocation = ['service', 'name'];
- const missingFieldsLocation: string[] = [];
- requiredFieldsLocation.forEach((field) => {
- if (!data?.location[field]) {
- missingFieldsLocation.push(field);
- }
- });
- if (missingFieldsLocation.length > 0) {
- const missingFieldsString = missingFieldsLocation.join(', ');
- const errorMsg = `Missing fields: ${missingFieldsString}`;
- throw new Error(errorMsg);
- }
- const resPermission = await getUserPermission(
- {
- text1: 'Would you like to download:',
- highlightedText: `${data?.filename}`,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (!accepted) throw new Error('User declined to save file');
- const a = document.createElement('a');
- let locationUrl = `/arbitrary/${data.location.service}/${data.location.name}`;
- if (data.location.identifier) {
- locationUrl = locationUrl + `/${data.location.identifier}`;
- }
- const endpoint = await createEndpoint(
- locationUrl + `?attachment=true&attachmentFilename=${data?.filename}`
- );
- a.href = endpoint;
- a.download = data.filename;
- document.body.appendChild(a);
- a.click();
- a.remove();
- return true;
- }
- const requiredFields = ['filename', 'blob'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const filename = data.filename;
- const blob = data.blob;
-
- const mimeType = blob.type || data.mimeType;
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:download_file', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: `${filename}`,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (!accepted) throw new Error('User declined to save file');
- showSaveFilePicker(
- {
- filename,
- mimeType,
- blob,
- },
- snackMethods
- );
-
- return true;
-
- if (accepted) {
- const mimeType = blob.type || data.mimeType;
- let backupExention = filename.split('.').pop();
- if (backupExention) {
- backupExention = '.' + backupExention;
- }
- const fileExtension = mimeToExtensionMap[mimeType] || backupExention;
- let fileHandleOptions = {};
- if (!mimeType) {
- throw new Error(
- i18n.t('question:message.error.mime_type', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- if (!fileExtension) {
- throw new Error(
- i18n.t('question:message.error.file_extension', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- if (fileExtension && mimeType) {
- fileHandleOptions = {
- accept: {
- [mimeType]: [fileExtension],
- },
- };
- }
-
- showSaveFilePicker(
- {
- filename,
- mimeType,
- blob,
- },
- snackMethods
- );
- return true;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_save_file', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('core:message.error.initiate_download', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const deployAt = async (data, isFromExtension) => {
- const requiredFields = [
- 'name',
- 'description',
- 'tags',
- 'creationBytes',
- 'amount',
- 'assetId',
- 'type',
- ];
-
- const missingFields: string[] = [];
-
- requiredFields.forEach((field) => {
- if (!data[field] && data[field] !== 0) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- try {
- const resDeployAt = await _deployAt(
- {
- name: data.name,
- description: data.description,
- tags: data.tags,
- creationBytes: data.creationBytes,
- amount: data.amount,
- assetId: data.assetId,
- atType: data.type,
- },
- isFromExtension
- );
- return resDeployAt;
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('group:message.error.group_join', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const getUserWallet = async (data, isFromExtension, appInfo) => {
- const requiredFields = ['coin'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const isGateway = await isRunningGateway();
-
- if (data?.coin === 'ARRR' && isGateway)
- throw new Error(
- i18n.t('question:message.error.gateway_wallet_local_node', {
- token: 'ARRR',
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- const value =
- (await getPermission(
- `qAPPAutoGetUserWallet-${appInfo?.name}-${data.coin}`
- )) || false;
- let skip = false;
- if (value) {
- skip = true;
- }
-
- let resPermission;
-
- if (!skip) {
- resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.get_wallet_info', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: `coin: ${data.coin}`,
- checkbox1: {
- value: true,
- label: i18n.t('question:always_retrieve_wallet', {
- postProcess: 'capitalizeFirstChar',
- }),
- },
- },
- isFromExtension
- );
- }
- const { accepted = false, checkbox1 = false } = resPermission || {};
-
- if (resPermission) {
- setPermission(
- `qAPPAutoGetUserWallet-${appInfo?.name}-${data.coin}`,
- checkbox1
- );
- }
-
- if (accepted || skip) {
- let coin = data.coin;
- let userWallet = {};
- let arrrAddress = '';
- const wallet = await getSaveWallet();
- const address = wallet.address0;
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- const arrrSeed58 = parsedData.arrrSeed58;
- if (coin === 'ARRR') {
- const bodyToString = arrrSeed58;
- const url = await createEndpoint(`/crosschain/arrr/walletaddress`);
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: bodyToString,
- });
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
- arrrAddress = res;
- }
- switch (coin) {
- case 'QORT':
- userWallet['address'] = address;
- userWallet['publickey'] = parsedData.publicKey;
- break;
- case 'BTC':
- userWallet['address'] = parsedData.btcAddress;
- userWallet['publickey'] = parsedData.btcPublicKey;
- break;
- case 'LTC':
- userWallet['address'] = parsedData.ltcAddress;
- userWallet['publickey'] = parsedData.ltcPublicKey;
- break;
- case 'DOGE':
- userWallet['address'] = parsedData.dogeAddress;
- userWallet['publickey'] = parsedData.dogePublicKey;
- break;
- case 'DGB':
- userWallet['address'] = parsedData.dgbAddress;
- userWallet['publickey'] = parsedData.dgbPublicKey;
- break;
- case 'RVN':
- userWallet['address'] = parsedData.rvnAddress;
- userWallet['publickey'] = parsedData.rvnPublicKey;
- break;
- case 'ARRR':
- await checkArrrSyncStatus(parsedData.arrrSeed58);
- userWallet['address'] = arrrAddress;
- break;
- default:
- break;
- }
- return userWallet;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const getWalletBalance = async (
- data,
- bypassPermission?: boolean,
- isFromExtension?: boolean,
- appInfo?: any
-) => {
- const requiredFields = ['coin'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const isGateway = await isRunningGateway();
-
- if (data?.coin === 'ARRR' && isGateway)
- throw new Error(
- i18n.t('question:message.error.gateway_balance_local_node', {
- token: 'ARRR',
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- const value =
- (await getPermission(
- `qAPPAutoWalletBalance-${appInfo?.name}-${data.coin}`
- )) || false;
- let skip = false;
- if (value) {
- skip = true;
- }
- let resPermission;
-
- if (!bypassPermission && !skip) {
- resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.fetch_balance', {
- coin: data.coin, // TODO highlight coin in the modal
- postProcess: 'capitalizeFirstChar',
- }),
- checkbox1: {
- value: true,
- label: i18n.t('question:always_retrieve_balance', {
- postProcess: 'capitalizeFirstChar',
- }),
- },
- },
- isFromExtension
- );
- }
- const { accepted = false, checkbox1 = false } = resPermission || {};
- if (resPermission) {
- setPermission(
- `qAPPAutoWalletBalance-${appInfo?.name}-${data.coin}`,
- checkbox1
- );
- }
- if (accepted || bypassPermission || skip) {
- let coin = data.coin;
- const wallet = await getSaveWallet();
- const address = wallet.address0;
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- if (coin === 'QORT') {
- let qortAddress = address;
- try {
- const url = await createEndpoint(`/addresses/balance/${qortAddress}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_balance', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- return res;
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.fetch_wallet', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } else {
- let _url = ``;
- let _body = null;
- switch (coin) {
- case 'BTC':
- _url = await createEndpoint(`/crosschain/btc/walletbalance`);
-
- _body = parsedData.btcPublicKey;
- break;
- case 'LTC':
- _url = await createEndpoint(`/crosschain/ltc/walletbalance`);
- _body = parsedData.ltcPublicKey;
- break;
- case 'DOGE':
- _url = await createEndpoint(`/crosschain/doge/walletbalance`);
- _body = parsedData.dogePublicKey;
- break;
- case 'DGB':
- _url = await createEndpoint(`/crosschain/dgb/walletbalance`);
- _body = parsedData.dgbPublicKey;
- break;
- case 'RVN':
- _url = await createEndpoint(`/crosschain/rvn/walletbalance`);
- _body = parsedData.rvnPublicKey;
- break;
- case 'ARRR':
- await checkArrrSyncStatus(parsedData.arrrSeed58);
- _url = await createEndpoint(`/crosschain/arrr/walletbalance`);
- _body = parsedData.arrrSeed58;
- break;
- default:
- break;
- }
- try {
- const response = await fetch(_url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: _body,
- });
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
- if (isNaN(Number(res))) {
- throw new Error(
- i18n.t('question:message.error.fetch_balance', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- } else {
- return (Number(res) / 1e8).toFixed(8);
- }
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.fetch_balance', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- }
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-const getPirateWallet = async (arrrSeed58) => {
- const isGateway = await isRunningGateway();
- if (isGateway) {
- throw new Error(
- i18n.t('question:message.error.gateway_retrieve_balance', {
- token: 'PIRATECHAIN',
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const bodyToString = arrrSeed58;
- await checkArrrSyncStatus(bodyToString);
- const url = await createEndpoint(`/crosschain/arrr/walletaddress`);
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: bodyToString,
- });
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
- return res;
-};
-
-export const getUserWalletFunc = async (coin) => {
- let userWallet = {};
- const wallet = await getSaveWallet();
- const address = wallet.address0;
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- switch (coin) {
- case 'QORT':
- userWallet['address'] = address;
- userWallet['publickey'] = parsedData.publicKey;
- break;
- case 'BTC':
- case 'BITCOIN':
- userWallet['address'] = parsedData.btcAddress;
- userWallet['publickey'] = parsedData.btcPublicKey;
- break;
- case 'LTC':
- case 'LITECOIN':
- userWallet['address'] = parsedData.ltcAddress;
- userWallet['publickey'] = parsedData.ltcPublicKey;
- break;
- case 'DOGE':
- case 'DOGECOIN':
- userWallet['address'] = parsedData.dogeAddress;
- userWallet['publickey'] = parsedData.dogePublicKey;
- break;
- case 'DGB':
- case 'DIGIBYTE':
- userWallet['address'] = parsedData.dgbAddress;
- userWallet['publickey'] = parsedData.dgbPublicKey;
- break;
- case 'RVN':
- case 'RAVENCOIN':
- userWallet['address'] = parsedData.rvnAddress;
- userWallet['publickey'] = parsedData.rvnPublicKey;
- break;
- case 'ARRR':
- case 'PIRATECHAIN':
- const arrrAddress = await getPirateWallet(parsedData.arrrSeed58);
- userWallet['address'] = arrrAddress;
- break;
- default:
- break;
- }
- return userWallet;
-};
-
-export const getUserWalletInfo = async (data, isFromExtension, appInfo) => {
- const requiredFields = ['coin'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- if (data?.coin === 'ARRR') {
- throw new Error(
- i18n.t('question:message.error.token_not_supported', {
- token: 'ARRR',
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const value =
- (await getPermission(`getUserWalletInfo-${appInfo?.name}-${data.coin}`)) ||
- false;
- let skip = false;
- if (value) {
- skip = true;
- }
- let resPermission;
-
- if (!skip) {
- resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.get_wallet_info', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: `coin: ${data.coin}`,
- checkbox1: {
- value: true,
- label: i18n.t('question:always_retrieve_wallet', {
- postProcess: 'capitalizeFirstChar',
- }),
- },
- },
- isFromExtension
- );
- }
- const { accepted = false, checkbox1 = false } = resPermission || {};
-
- if (resPermission) {
- setPermission(`getUserWalletInfo-${appInfo?.name}-${data.coin}`, checkbox1);
- }
-
- if (accepted || skip) {
- let coin = data.coin;
- let walletKeys = await getUserWalletFunc(coin);
-
- const _url = await createEndpoint(
- `/crosschain/` + data.coin.toLowerCase() + `/addressinfos`
- );
- let _body = { xpub58: walletKeys['publickey'] };
- try {
- const response = await fetch(_url, {
- method: 'POST',
- headers: {
- Accept: '*/*',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(_body),
- });
- if (!response?.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_wallet_info', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
-
- return res;
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.fetch_wallet', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const getUserWalletTransactions = async (
- data,
- isFromExtension,
- appInfo
-) => {
- const requiredFields = ['coin'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const value =
- (await getPermission(
- `getUserWalletTransactions-${appInfo?.name}-${data.coin}`
- )) || false;
- let skip = false;
- if (value) {
- skip = true;
- }
- let resPermission;
-
- if (!skip) {
- resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.get_wallet_transactions', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: `coin: ${data.coin}`,
- checkbox1: {
- value: true,
- label: i18n.t('question:always_retrieve_wallet_transactions', {
- postProcess: 'capitalizeFirstChar',
- }),
- },
- },
- isFromExtension
- );
- }
- const { accepted = false, checkbox1 = false } = resPermission || {};
-
- if (resPermission) {
- setPermission(
- `getUserWalletTransactions-${appInfo?.name}-${data.coin}`,
- checkbox1
- );
- }
-
- if (accepted || skip) {
- const coin = data.coin;
- const walletKeys = await getUserWalletFunc(coin);
- let publicKey;
- if (data?.coin === 'ARRR') {
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- publicKey = parsedData.arrrSeed58;
- } else {
- publicKey = walletKeys['publickey'];
- }
-
- const _url = await createEndpoint(
- `/crosschain/` + data.coin.toLowerCase() + `/wallettransactions`
- );
- const _body = publicKey;
- try {
- const response = await fetch(_url, {
- method: 'POST',
- headers: {
- Accept: '*/*',
- 'Content-Type': 'application/json',
- },
- body: _body,
- });
- if (!response?.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_wallet_transactions', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
-
- return res;
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.fetch_wallet_transactions', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const getCrossChainServerInfo = async (data) => {
- const requiredFields = ['coin'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const _url = `/crosschain/` + data.coin.toLowerCase() + `/serverinfos`;
- try {
- const url = await createEndpoint(_url);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_generic', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
- return res.servers;
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.server_info', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const getTxActivitySummary = async (data) => {
- const requiredFields = ['coin'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const coin = data.coin;
- const url = `/crosschain/txactivity?foreignBlockchain=${coin}`; // No apiKey here
-
- try {
- const endpoint = await createEndpoint(url);
- const response = await fetch(endpoint, {
- method: 'POST',
- headers: {
- Accept: '*/*',
- 'Content-Type': 'application/json',
- },
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_generic', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
- return res; // Return full response here
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.transaction_activity_summary', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const getForeignFee = async (data) => {
- const requiredFields = ['coin', 'type'];
- const missingFields: string[] = [];
-
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const { coin, type } = data;
- const url = `/crosschain/${coin.toLowerCase()}/${type}`;
-
- try {
- const endpoint = await createEndpoint(url);
- const response = await fetch(endpoint, {
- method: 'GET',
- headers: {
- Accept: '*/*',
- 'Content-Type': 'application/json',
- },
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_generic', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
- return res; // Return full response here
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.get_foreign_fee', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-function calculateRateFromFee(totalFee, sizeInBytes) {
- const fee = (totalFee / sizeInBytes) * 1000;
- return fee.toFixed(0);
-}
-
-export const updateForeignFee = async (data, isFromExtension) => {
- const isGateway = await isRunningGateway();
- if (isGateway) {
- throw new Error(
- i18n.t('question:message.generic.no_action_public_node', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const requiredFields = ['coin', 'type', 'value'];
- const missingFields: string[] = [];
-
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const { coin, type, value } = data;
-
- const text3 =
- type === 'feerequired'
- ? i18n.t('question:sats', {
- amount: value,
- postProcess: 'capitalizeFirstChar',
- })
- : i18n.t('question:sats_per_kb', {
- amount: value,
- postProcess: 'capitalizeFirstChar',
- });
- const text4 =
- type === 'feerequired'
- ? i18n.t('question:message.generic.calculate_fee', {
- amount: value,
- rate: calculateRateFromFee(value, 300),
- postProcess: 'capitalizeFirstChar',
- })
- : '';
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.update_foreign_fee', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: `type: ${type === 'feerequired' ? 'unlocking' : 'locking'}`,
- text3: i18n.t('question:value', {
- value: text3,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('question:coin', {
- coin: coin,
- postProcess: 'capitalizeFirstChar',
- }),
- },
- isFromExtension
- );
-
- const { accepted } = resPermission;
- if (!accepted) {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const url = `/crosschain/${coin.toLowerCase()}/update${type}`;
- const valueStringified = JSON.stringify(+value);
-
- const endpoint = await createEndpoint(url);
- const response = await fetch(endpoint, {
- method: 'POST',
- headers: {
- Accept: '*/*',
- 'Content-Type': 'application/json',
- },
- body: valueStringified,
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.update_foreign_fee', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
- return res; // Return full response here
-};
-
-export const getServerConnectionHistory = async (data) => {
- const requiredFields = ['coin'];
- const missingFields: string[] = [];
-
- // Validate required fields
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const coin = data.coin.toLowerCase();
- const url = `/crosschain/${coin.toLowerCase()}/serverconnectionhistory`;
-
- try {
- const endpoint = await createEndpoint(url); // Assuming createEndpoint is available
- const response = await fetch(endpoint, {
- method: 'GET',
- headers: {
- Accept: '*/*',
- 'Content-Type': 'application/json',
- },
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_connection_history', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
-
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
-
- return res; // Return full response here
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.fetch_connection_history', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const setCurrentForeignServer = async (data, isFromExtension) => {
- const isGateway = await isRunningGateway();
- if (isGateway) {
- throw new Error(
- i18n.t('question:message.generic.no_action_public_node', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const requiredFields = ['coin'];
- const missingFields: string[] = [];
-
- // Validate required fields
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const { coin, host, port, type } = data;
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.set_current_server', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:server_type', {
- type: type,
- postProcess: 'capitalizeFirstChar',
- }),
- text3: i18n.t('question:server_host', {
- host: host,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('question:coin', {
- coin: coin,
- postProcess: 'capitalizeFirstChar',
- }),
- },
- isFromExtension
- );
-
- const { accepted } = resPermission;
- if (!accepted) {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const body = {
- hostName: host,
- port: port,
- connectionType: type,
- };
-
- const url = `/crosschain/${coin.toLowerCase()}/setcurrentserver`;
-
- const endpoint = await createEndpoint(url); // Assuming createEndpoint is available
- const response = await fetch(endpoint, {
- method: 'POST',
- headers: {
- Accept: '*/*',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(body),
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.server_current_set', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
-
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
-
- return res; // Return the full response
-};
-
-export const addForeignServer = async (data, isFromExtension) => {
- const isGateway = await isRunningGateway();
- if (isGateway) {
- throw new Error(
- i18n.t('question:message.generic.no_action_public_node', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const requiredFields = ['coin'];
- const missingFields: string[] = [];
-
- // Validate required fields
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const { coin, host, port, type } = data;
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.server_add', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:server_type', {
- type: type,
- postProcess: 'capitalizeFirstChar',
- }),
- text3: i18n.t('question:server_host', {
- host: host,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('question:coin', {
- coin: coin,
- postProcess: 'capitalizeFirstChar',
- }),
- },
- isFromExtension
- );
-
- const { accepted } = resPermission;
- if (!accepted) {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const body = {
- hostName: host,
- port: port,
- connectionType: type,
- };
-
- const url = `/crosschain/${coin.toLowerCase()}/addserver`;
-
- const endpoint = await createEndpoint(url); // Assuming createEndpoint is available
- const response = await fetch(endpoint, {
- method: 'POST',
- headers: {
- Accept: '*/*',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(body),
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.server_current_add', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
-
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
-
- return res; // Return the full response
-};
-
-export const removeForeignServer = async (data, isFromExtension) => {
- const isGateway = await isRunningGateway();
- if (isGateway) {
- throw new Error(
- i18n.t('question:message.generic.no_action_public_node', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const requiredFields = ['coin'];
- const missingFields: string[] = [];
-
- // Validate required fields
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const { coin, host, port, type } = data;
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.server_remove', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:server_type', {
- type: type,
- postProcess: 'capitalizeFirstChar',
- }),
- text3: i18n.t('question:server_host', {
- host: host,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('question:coin', {
- coin: coin,
- postProcess: 'capitalizeFirstChar',
- }),
- },
- isFromExtension
- );
-
- const { accepted } = resPermission;
- if (!accepted) {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const body = {
- hostName: host,
- port: port,
- connectionType: type,
- };
-
- const url = `/crosschain/${coin.toLowerCase()}/removeserver`;
-
- const endpoint = await createEndpoint(url); // Assuming createEndpoint is available
- const response = await fetch(endpoint, {
- method: 'POST',
- headers: {
- Accept: '*/*',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(body),
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.server_remove', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
-
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
-
- return res; // Return the full response
-};
-
-export const getDaySummary = async () => {
- const url = `/admin/summary`; // Simplified endpoint URL
-
- try {
- const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL
- const response = await fetch(endpoint, {
- method: 'GET',
- headers: {
- Accept: '*/*',
- },
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.retrieve_summary', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
-
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
-
- return res; // Return the full response
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.retrieve_summary', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const getNodeInfo = async () => {
- const url = `/admin/info`; // Simplified endpoint URL
-
- try {
- const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL
- const response = await fetch(endpoint, {
- method: 'GET',
- headers: {
- Accept: '*/*',
- },
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.node_info', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
-
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
-
- return res; // Return the full response
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.node_info', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const getNodeStatus = async () => {
- const url = `/admin/status`; // Simplified endpoint URL
-
- try {
- const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL
- const response = await fetch(endpoint, {
- method: 'GET',
- headers: {
- Accept: '*/*',
- },
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.node_status', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
-
- if (res?.error && res?.message) {
- throw new Error(res.message);
- }
-
- return res; // Return the full response
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.node_status', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const getArrrSyncStatus = async () => {
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- const arrrSeed = parsedData.arrrSeed58;
- const url = `/crosschain/arrr/syncstatus`; // Simplified endpoint URL
-
- try {
- const endpoint = await createEndpoint(url); // Assuming createEndpoint is available for constructing the full URL
- const response = await fetch(endpoint, {
- method: 'POST',
- headers: {
- Accept: '*/*',
- },
- body: arrrSeed,
- });
-
- let res;
-
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
-
- return res; // Return the full response
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.retrieve_sync_status', {
- token: 'ARRR',
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const sendCoin = async (data, isFromExtension) => {
- const requiredFields = ['coin', 'amount'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- if (!data?.destinationAddress && !data?.recipient) {
- throw new Error(
- i18n.t('question:message.error.missing_fields', {
- fields: 'recipient',
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- let checkCoin = data.coin;
- const wallet = await getSaveWallet();
- const address = wallet.address0;
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- const isGateway = await isRunningGateway();
-
- if (checkCoin !== 'QORT' && isGateway)
- throw new Error(
- i18n.t('question:message.error.gateway_non_qort_local_node', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- if (checkCoin === 'QORT') {
- // Params: data.coin, data.recipient, data.amount, data.fee
- // TODO: prompt user to send. If they confirm, call `POST /crosschain/:coin/send`, or for QORT, broadcast a PAYMENT transaction
- // then set the response string from the core to the `response` variable (defined above)
- // If they decline, send back JSON that includes an `error` key, such as `{"error": "User declined request"}`
- const amount = Number(data.amount);
- const recipient = data?.recipient || data.destinationAddress;
-
- const url = await createEndpoint(`/addresses/balance/${address}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_balance', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let walletBalance;
- try {
- walletBalance = await response.clone().json();
- } catch (e) {
- walletBalance = await response.text();
- }
- if (isNaN(Number(walletBalance))) {
- const errorMsg = i18n.t('question:message.error.fetch_balance_token', {
- token: 'QORT',
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const transformDecimals = (Number(walletBalance) * QORT_DECIMALS).toFixed(
- 0
- );
- const walletBalanceDecimals = Number(transformDecimals);
- const amountDecimals = Number(amount) * QORT_DECIMALS;
- const fee: number = await sendQortFee();
- if (amountDecimals + fee * QORT_DECIMALS > walletBalanceDecimals) {
- const errorMsg = i18n.t('question:message.error.insufficient_funds', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- if (amount <= 0) {
- const errorMsg = i18n.t('core:message.error.invalid_amount', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- if (recipient.length === 0) {
- const errorMsg = i18n.t('question:message.error.empty_receiver', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.send_coins', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:to_recipient', {
- recipient: recipient,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: `${amount} ${checkCoin}`,
- fee: fee,
- confirmCheckbox: true,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const makePayment = await sendCoinFunc(
- { amount, password: null, receiver: recipient },
- true
- );
- return makePayment.res?.data;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } else if (checkCoin === 'BTC') {
- const amount = Number(data.amount);
- const recipient = data?.recipient || data.destinationAddress;
- const xprv58 = parsedData.btcPrivateKey;
- const feePerByte = data.fee ? data.fee : btcFeePerByte;
-
- const btcWalletBalance = await getWalletBalance({ coin: checkCoin }, true);
-
- if (isNaN(Number(btcWalletBalance))) {
- throw new Error(
- i18n.t('question:message.error.fetch_balance_token', {
- token: 'BTC',
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const btcWalletBalanceDecimals = Number(btcWalletBalance);
- const btcAmountDecimals = Number(amount);
- const fee = feePerByte * 500; // default 0.00050000
- if (btcAmountDecimals + fee > btcWalletBalanceDecimals) {
- throw new Error(
- i18n.t('question:message.error.insufficient_funds', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.send_coins', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:to_recipient', {
- recipient: recipient,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: `${amount} ${checkCoin}`,
- foreignFee: `${fee} BTC`,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const opts = {
- xprv58: xprv58,
- receivingAddress: recipient,
- bitcoinAmount: amount,
- feePerByte: feePerByte,
- };
- const url = await createEndpoint(`/crosschain/btc/send`);
-
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(opts),
- });
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.send', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } else if (checkCoin === 'LTC') {
- const amount = Number(data.amount);
- const recipient = data?.recipient || data.destinationAddress;
- const xprv58 = parsedData.ltcPrivateKey;
- const feePerByte = data.fee ? data.fee : ltcFeePerByte;
- const ltcWalletBalance = await getWalletBalance({ coin: checkCoin }, true);
-
- if (isNaN(Number(ltcWalletBalance))) {
- const errorMsg = i18n.t('question:message.error.fetch_balance_token', {
- token: 'LTC',
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const ltcWalletBalanceDecimals = Number(ltcWalletBalance);
- const ltcAmountDecimals = Number(amount);
- const fee = feePerByte * 1000; // default 0.00030000
- if (ltcAmountDecimals + fee > ltcWalletBalanceDecimals) {
- throw new Error(
- i18n.t('question:message.error.insufficient_funds', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.send_coins', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:to_recipient', {
- recipient: recipient,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: `${amount} ${checkCoin}`,
- foreignFee: `${fee} LTC`,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const url = await createEndpoint(`/crosschain/ltc/send`);
- const opts = {
- xprv58: xprv58,
- receivingAddress: recipient,
- litecoinAmount: amount,
- feePerByte: feePerByte,
- };
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(opts),
- });
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.send', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } else if (checkCoin === 'DOGE') {
- const amount = Number(data.amount);
- const recipient = data?.recipient || data.destinationAddress;
- const xprv58 = parsedData.dogePrivateKey;
- const feePerByte = data.fee ? data.fee : dogeFeePerByte;
- const dogeWalletBalance = await getWalletBalance({ coin: checkCoin }, true);
- if (isNaN(Number(dogeWalletBalance))) {
- const errorMsg = i18n.t('question:message.error.fetch_balance_token', {
- token: 'DOGE',
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const dogeWalletBalanceDecimals = Number(dogeWalletBalance);
- const dogeAmountDecimals = Number(amount);
- const fee = feePerByte * 5000; // default 0.05000000
- if (dogeAmountDecimals + fee > dogeWalletBalanceDecimals) {
- const errorMsg = i18n.t('question:message.error.insufficient_funds', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.send_coins', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:to_recipient', {
- recipient: recipient,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: `${amount} ${checkCoin}`,
- foreignFee: `${fee} DOGE`,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const opts = {
- xprv58: xprv58,
- receivingAddress: recipient,
- dogecoinAmount: amount,
- feePerByte: feePerByte,
- };
- const url = await createEndpoint(`/crosschain/doge/send`);
-
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(opts),
- });
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.send', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } else if (checkCoin === 'DGB') {
- const amount = Number(data.amount);
- const recipient = data?.recipient || data.destinationAddress;
- const xprv58 = parsedData.dbgPrivateKey;
- const feePerByte = data.fee ? data.fee : dgbFeePerByte;
- const dgbWalletBalance = await getWalletBalance({ coin: checkCoin }, true);
- if (isNaN(Number(dgbWalletBalance))) {
- const errorMsg = i18n.t('question:message.error.fetch_balance_token', {
- token: 'DGB',
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const dgbWalletBalanceDecimals = Number(dgbWalletBalance);
- const dgbAmountDecimals = Number(amount);
- const fee = feePerByte * 500; // default 0.00005000
- if (dgbAmountDecimals + fee > dgbWalletBalanceDecimals) {
- const errorMsg = i18n.t('question:message.error.insufficient_funds', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.send_coins', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: `To: ${recipient}`,
- highlightedText: `${amount} ${checkCoin}`,
- foreignFee: `${fee} DGB`,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const opts = {
- xprv58: xprv58,
- receivingAddress: recipient,
- digibyteAmount: amount,
- feePerByte: feePerByte,
- };
- const url = await createEndpoint(`/crosschain/dgb/send`);
-
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(opts),
- });
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.send', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } else if (checkCoin === 'RVN') {
- const amount = Number(data.amount);
- const recipient = data?.recipient || data.destinationAddress;
- const xprv58 = parsedData.rvnPrivateKey;
- const feePerByte = data.fee ? data.fee : rvnFeePerByte;
- const rvnWalletBalance = await getWalletBalance({ coin: checkCoin }, true);
- if (isNaN(Number(rvnWalletBalance))) {
- const errorMsg = i18n.t('question:message.error.fetch_balance_token', {
- token: 'RVN',
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const rvnWalletBalanceDecimals = Number(rvnWalletBalance);
- const rvnAmountDecimals = Number(amount);
- const fee = feePerByte * 500; // default 0.00562500
- if (rvnAmountDecimals + fee > rvnWalletBalanceDecimals) {
- const errorMsg = i18n.t('question:message.error.insufficient_funds', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.send_coins', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: `To: ${recipient}`,
- highlightedText: `${amount} ${checkCoin}`,
- foreignFee: `${fee} RVN`,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const opts = {
- xprv58: xprv58,
- receivingAddress: recipient,
- ravencoinAmount: amount,
- feePerByte: feePerByte,
- };
- const url = await createEndpoint(`/crosschain/rvn/send`);
-
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(opts),
- });
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.send', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } else if (checkCoin === 'ARRR') {
- const amount = Number(data.amount);
- const recipient = data?.recipient || data.destinationAddress;
- const memo = data?.memo;
- const arrrWalletBalance = await getWalletBalance({ coin: checkCoin }, true);
-
- if (isNaN(Number(arrrWalletBalance))) {
- const errorMsg = i18n.t('question:message.error.fetch_balance_token', {
- token: 'ARR',
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const arrrWalletBalanceDecimals = Number(arrrWalletBalance);
- const arrrAmountDecimals = Number(amount);
- const fee = 0.0001;
- if (arrrAmountDecimals + fee > arrrWalletBalanceDecimals) {
- const errorMsg = i18n.t('question:message.error.insufficient_funds', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.send_coins', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: `To: ${recipient}`,
- highlightedText: `${amount} ${checkCoin}`,
- foreignFee: `${fee} ARRR`,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
-
- if (accepted) {
- const opts = {
- entropy58: parsedData.arrrSeed58,
- receivingAddress: recipient,
- arrrAmount: amount,
- memo: memo,
- };
- const url = await createEndpoint(`/crosschain/arrr/send`);
-
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(opts),
- });
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.send', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- }
-};
-
-function calculateFeeFromRate(feePerKb, sizeInBytes) {
- return (feePerKb / 1000) * sizeInBytes;
-}
-
-const getBuyingFees = async (foreignBlockchain) => {
- const ticker = sellerForeignFee[foreignBlockchain].ticker;
- if (!ticker) throw new Error('invalid foreign blockchain');
- const unlockFee = await getForeignFee({
- coin: ticker,
- type: 'feerequired',
- });
- const lockFee = await getForeignFee({
- coin: ticker,
- type: 'feekb',
- });
- return {
- ticker: ticker,
- lock: {
- sats: lockFee,
- fee: lockFee / QORT_DECIMALS,
- },
- unlock: {
- sats: unlockFee,
- fee: unlockFee / QORT_DECIMALS,
- feePerKb: +calculateRateFromFee(+unlockFee, 300) / QORT_DECIMALS,
- },
- };
-};
-
-export const createBuyOrder = async (data, isFromExtension) => {
- const requiredFields = ['crosschainAtInfo', 'foreignBlockchain'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const isGateway = await isRunningGateway();
- const foreignBlockchain = data.foreignBlockchain;
- const atAddresses = data.crosschainAtInfo?.map(
- (order) => order.qortalAtAddress
- );
-
- const atPromises = atAddresses.map((atAddress) =>
- requestQueueGetAtAddresses.enqueue(async () => {
- const url = await createEndpoint(`/crosschain/trade/${atAddress}`);
- const resAddress = await fetch(url);
- const resData = await resAddress.json();
- if (foreignBlockchain !== resData?.foreignBlockchain) {
- throw new Error(
- i18n.t('core:message.error.same_foreign_blockchain', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- return resData;
- })
- );
-
- const crosschainAtInfo = await Promise.all(atPromises);
-
- try {
- const buyingFees = await getBuyingFees(foreignBlockchain);
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.buy_order', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:permission.buy_order_quantity', {
- quantity: atAddresses?.length,
- postProcess: 'capitalizeFirstChar',
- }),
- text3: i18n.t('question:permission.buy_order_ticker', {
- qort_amount: crosschainAtInfo?.reduce((latest, cur) => {
- return latest + +cur?.qortAmount;
- }, 0),
- foreign_amount: roundUpToDecimals(
- crosschainAtInfo?.reduce((latest, cur) => {
- return latest + +cur?.expectedForeignAmount;
- }, 0)
- ),
- ticker: buyingFees.ticker,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('auth:node.using_public_gateway', {
- gateway: isGateway,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: '',
- html: `
-
-
-
-
-
${i18n.t('question:total_unlocking_fee', {
- postProcess: 'capitalizeFirstChar',
- })}
-
${(+buyingFees?.unlock?.fee * atAddresses?.length)?.toFixed(8)} ${buyingFees.ticker}
-
- ${i18n.t('question:permission.buy_order_fee_estimation', {
- quantity: atAddresses?.length,
- fee: buyingFees?.unlock?.feePerKb?.toFixed(8),
- ticker: buyingFees.ticker,
- postProcess: 'capitalizeFirstChar',
- })}
-
-
${i18n.t('question:total_locking_fee', {
- postProcess: 'capitalizeFirstChar',
- })}
-
${i18n.t('question:permission.buy_order_per_kb', {
- fee: +buyingFees?.lock.fee.toFixed(8),
- ticker: buyingFees.ticker,
- postProcess: 'capitalizeFirstChar',
- })}
-
-
-
-`,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const resBuyOrder = await createBuyOrderTx({
- crosschainAtInfo,
- isGateway,
- foreignBlockchain,
- });
- return resBuyOrder;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.buy_order', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-const cancelTradeOfferTradeBot = async (body, keyPair) => {
- const txn = new DeleteTradeOffer().createTransaction(body);
- const url = await createEndpoint(`/crosschain/tradeoffer`);
- const bodyToString = JSON.stringify(txn);
-
- const deleteTradeBotResponse = await fetch(url, {
- method: 'DELETE',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: bodyToString,
- });
-
- if (!deleteTradeBotResponse.ok) {
- throw new Error(
- i18n.t('question:message.error.update_tradebot', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- const unsignedTxn = await deleteTradeBotResponse.text();
- const signedTxnBytes = await signTradeBotTransaction(unsignedTxn, keyPair);
- const signedBytes = Base58.encode(signedTxnBytes);
-
- let res;
- try {
- res = await processTransactionVersion2(signedBytes);
- } catch (error) {
- return {
- error: i18n.t('question:message.error.cancel_sell_order', {
- postProcess: 'capitalizeFirstChar',
- }),
- failedTradeBot: {
- atAddress: body.atAddress,
- creatorAddress: body.creatorAddress,
- },
- };
- }
- if (res?.error) {
- return {
- error: i18n.t('question:message.error.cancel_sell_order', {
- postProcess: 'capitalizeFirstChar',
- }),
- failedTradeBot: {
- atAddress: body.atAddress,
- creatorAddress: body.creatorAddress,
- },
- };
- }
- if (res?.signature) {
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.error.cancel_sell_order', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-const findFailedTradebot = async (createBotCreationTimestamp, body) => {
- //wait 5 secs
- const wallet = await getSaveWallet();
- const address = wallet.address0;
- await new Promise((res) => {
- setTimeout(() => {
- res(null);
- }, 5000);
- });
- const url = await createEndpoint(
- `/crosschain/tradebot?foreignBlockchain=LITECOIN`
- );
-
- const tradeBotsReponse = await fetch(url, {
- method: 'GET',
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- const data = await tradeBotsReponse.json();
- const latestItem2 = data
- .filter((item) => item.creatorAddress === address)
- .sort((a, b) => b.timestamp - a.timestamp)[0];
- const latestItem = data
- .filter(
- (item) =>
- item.creatorAddress === address &&
- +item.foreignAmount === +body.foreignAmount
- )
- .sort((a, b) => b.timestamp - a.timestamp)[0];
- if (
- latestItem &&
- createBotCreationTimestamp - latestItem.timestamp <= 5000 &&
- createBotCreationTimestamp > latestItem.timestamp // Ensure latestItem's timestamp is before createBotCreationTimestamp
- ) {
- return latestItem;
- } else {
- return null;
- }
-};
-const tradeBotCreateRequest = async (body, keyPair) => {
- const txn = new TradeBotCreateRequest().createTransaction(body);
- const url = await createEndpoint(`/crosschain/tradebot/create`);
- const bodyToString = JSON.stringify(txn);
-
- const unsignedTxnResponse = await fetch(url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: bodyToString,
- });
- if (!unsignedTxnResponse.ok)
- throw new Error(
- i18n.t('question:message.error.create_tradebot', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const createBotCreationTimestamp = Date.now();
- const unsignedTxn = await unsignedTxnResponse.text();
- const signedTxnBytes = await signTradeBotTransaction(unsignedTxn, keyPair);
- const signedBytes = Base58.encode(signedTxnBytes);
-
- let res;
- try {
- res = await processTransactionVersion2(signedBytes);
- } catch (error) {
- const findFailedTradeBot = await findFailedTradebot(
- createBotCreationTimestamp,
- body
- );
- return {
- error: i18n.t('question:message.error.create_sell_order', {
- postProcess: 'capitalizeFirstChar',
- }),
- failedTradeBot: findFailedTradeBot,
- };
- }
-
- if (res?.signature) {
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.error.create_sell_order', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const createSellOrder = async (data, isFromExtension) => {
- const requiredFields = ['qortAmount', 'foreignBlockchain', 'foreignAmount'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const parsedForeignAmount = Number(data.foreignAmount)?.toFixed(8);
-
- const receivingAddress = await getUserWalletFunc(data.foreignBlockchain);
- try {
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.sell_order', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:permission.order_detail', {
- qort_amount: data.qortAmount,
- foreign_amount: parsedForeignAmount,
- ticker: data.foreignBlockchain,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: '0.02',
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- const userPublicKey = parsedData.publicKey;
- const uint8PrivateKey = Base58.decode(parsedData.privateKey);
- const uint8PublicKey = Base58.decode(parsedData.publicKey);
- const keyPair = {
- privateKey: uint8PrivateKey,
- publicKey: uint8PublicKey,
- };
- const response = await tradeBotCreateRequest(
- {
- creatorPublicKey: userPublicKey,
- qortAmount: parseFloat(data.qortAmount),
- fundingQortAmount: parseFloat(data.qortAmount) + 0.01,
- foreignBlockchain: data.foreignBlockchain,
- foreignAmount: parseFloat(parsedForeignAmount),
- tradeTimeout: 120,
- receivingAddress: receivingAddress.address,
- },
- keyPair
- );
-
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.submit_sell_order', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const cancelSellOrder = async (data, isFromExtension) => {
- const requiredFields = ['atAddress'];
- const missingFields: string[] = [];
-
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const url = await createEndpoint(`/crosschain/trade/${data.atAddress}`);
- const resAddress = await fetch(url);
- const resData = await resAddress.json();
-
- if (!resData?.qortalAtAddress)
- throw new Error(
- i18n.t('question:message.error.at_info', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- try {
- const fee = await getFee('MESSAGE');
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.cancel_sell_order', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:permission.order_detail', {
- qort_amount: resData.qortAmount,
- foreign_amount: resData.expectedForeignAmount,
- ticker: resData.foreignBlockchain,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- const userPublicKey = parsedData.publicKey;
- const uint8PrivateKey = Base58.decode(parsedData.privateKey);
- const uint8PublicKey = Base58.decode(parsedData.publicKey);
- const keyPair = {
- privateKey: uint8PrivateKey,
- publicKey: uint8PublicKey,
- };
- const response = await cancelTradeOfferTradeBot(
- {
- creatorPublicKey: userPublicKey,
- atAddress: data.atAddress,
- },
- keyPair
- );
-
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- } catch (error) {
- throw new Error(
- error?.message ||
- i18n.t('question:message.error.submit_sell_order', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const openNewTab = async (data, isFromExtension) => {
- const requiredFields = ['qortalLink'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const res = extractComponents(data.qortalLink);
- if (res) {
- const { service, name, identifier, path } = res;
- if (!service && !name)
- throw new Error(
- i18n.t('auth:message.error.invalid_qortal_link', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- executeEvent('addTab', { data: { service, name, identifier, path } });
- executeEvent('open-apps-mode', {});
- return true;
- } else {
- throw new Error(
- i18n.t('auth:message.error.invalid_qortal_link', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const adminAction = async (data, isFromExtension) => {
- const requiredFields = ['type'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- // For actions that require a value, check for 'value' field
- const actionsRequiringValue = [
- 'addpeer',
- 'removepeer',
- 'forcesync',
- 'addmintingaccount',
- 'removemintingaccount',
- ];
- if (actionsRequiringValue.includes(data.type.toLowerCase()) && !data.value) {
- missingFields.push('value');
- }
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const isGateway = await isRunningGateway();
- if (isGateway) {
- throw new Error(
- i18n.t('question:message.generic.no_action_public_node', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- let apiEndpoint = '';
- let method = 'GET'; // Default method
- let includeValueInBody = false;
- switch (data.type.toLowerCase()) {
- case 'stop':
- apiEndpoint = await createEndpoint('/admin/stop');
- break;
- case 'restart':
- apiEndpoint = await createEndpoint('/admin/restart');
- break;
- case 'bootstrap':
- apiEndpoint = await createEndpoint('/admin/bootstrap');
- break;
- case 'addmintingaccount':
- apiEndpoint = await createEndpoint('/admin/mintingaccounts');
- method = 'POST';
- includeValueInBody = true;
- break;
- case 'removemintingaccount':
- apiEndpoint = await createEndpoint('/admin/mintingaccounts');
- method = 'DELETE';
- includeValueInBody = true;
- break;
- case 'forcesync':
- apiEndpoint = await createEndpoint('/admin/forcesync');
- method = 'POST';
- includeValueInBody = true;
- break;
- case 'addpeer':
- apiEndpoint = await createEndpoint('/peers');
- method = 'POST';
- includeValueInBody = true;
- break;
- case 'removepeer':
- apiEndpoint = await createEndpoint('/peers');
- method = 'DELETE';
- includeValueInBody = true;
- break;
- default:
- throw new Error(
- i18n.t('question:message.error.unknown_admin_action_type', {
- type: data.type,
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- // Prepare the permission prompt text
- let permissionText = i18n.t('question:permission.perform_admin_action', {
- type: data.type,
- postProcess: 'capitalizeFirstChar',
- });
-
- if (data.value) {
- permissionText +=
- ' ' +
- i18n.t('question:permission.perform_admin_action_with_value', {
- value: data.value,
- postProcess: 'capitalizeFirstChar',
- });
- }
-
- const resPermission = await getUserPermission(
- {
- text1: permissionText,
- },
- isFromExtension
- );
-
- const { accepted } = resPermission;
-
- if (accepted) {
- // Set up options for the API call
- const options: RequestInit = {
- method: method,
- headers: {},
- };
- if (includeValueInBody) {
- options.headers['Content-Type'] = 'text/plain';
- options.body = data.value;
- }
- const response = await fetch(apiEndpoint, options);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.perform_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- let res;
- try {
- res = await response.clone().json();
- } catch (e) {
- res = await response.text();
- }
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const signTransaction = async (data, isFromExtension) => {
- const requiredFields = ['unsignedBytes'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const shouldProcess = data?.process || false;
- const _url = await createEndpoint(
- '/transactions/decode?ignoreValidityChecks=false'
- );
-
- const _body = data.unsignedBytes;
- const response = await fetch(_url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: _body,
- });
-
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.decode_transaction', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const decodedData = await response.json();
- const resPermission = await getUserPermission(
- {
- text1: shouldProcess
- ? i18n.t('question:permission.sign_process_transaction', {
- postProcess: 'capitalizeFirstChar',
- })
- : i18n.t('question:permission.sign_transaction', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t(
- 'question:message.generic.read_transaction_carefully',
- { postProcess: 'capitalizeFirstChar' }
- ),
- text2: `Tx type: ${decodedData.type}`,
- json: decodedData,
- },
- isFromExtension
- );
-
- const { accepted } = resPermission;
- if (accepted) {
- let urlConverted = await createEndpoint('/transactions/convert');
-
- const responseConverted = await fetch(urlConverted, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: data.unsignedBytes,
- });
- 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 convertedBytes = await responseConverted.text();
- const txBytes = Base58.decode(data.unsignedBytes);
- const _arbitraryBytesBuffer = Object.keys(txBytes).map(function (key) {
- return txBytes[key];
- });
- const arbitraryBytesBuffer = new Uint8Array(_arbitraryBytesBuffer);
- const txByteSigned = Base58.decode(convertedBytes);
- const _bytesForSigningBuffer = Object.keys(txByteSigned).map(
- function (key) {
- return txByteSigned[key];
- }
- );
- const bytesForSigningBuffer = new Uint8Array(_bytesForSigningBuffer);
- const signature = nacl.sign.detached(
- bytesForSigningBuffer,
- keyPair.privateKey
- );
- const signedBytes = utils.appendBuffer(arbitraryBytesBuffer, signature);
- const signedBytesToBase58 = Base58.encode(signedBytes);
- if (!shouldProcess) {
- return signedBytesToBase58;
- }
- const res = await processTransactionVersion2(signedBytesToBase58);
- if (!res?.signature)
- throw new Error(
- res?.message ||
- i18n.t('question:message.error.process_transaction', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- return res;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-const missingFieldsFunc = (data, requiredFields) => {
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-};
-
-const encode = (value) => encodeURIComponent(value.trim()); // Helper to encode values
-const buildQueryParams = (data) => {
- const allowedParams = [
- 'name',
- 'service',
- 'identifier',
- 'mimeType',
- 'fileName',
- 'encryptionType',
- 'key',
- ];
- return Object.entries(data)
- .map(([key, value]) => {
- if (
- value === undefined ||
- value === null ||
- value === false ||
- !allowedParams.includes(key)
- )
- return null; // Skip null, undefined, or false
- if (typeof value === 'boolean') return `${key}=${value}`; // Handle boolean values
- return `${key}=${encode(value)}`; // Encode other values
- })
- .filter(Boolean) // Remove null values
- .join('&'); // Join with `&`
-};
-export const createAndCopyEmbedLink = async (data, isFromExtension) => {
- const requiredFields = ['type'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- switch (data.type) {
- case 'POLL': {
- missingFieldsFunc(data, ['type', 'name']);
-
- const queryParams = [
- `name=${encode(data.name)}`,
- data.ref ? `ref=${encode(data.ref)}` : null, // Add only if ref exists
- ]
- .filter(Boolean) // Remove null values
- .join('&'); // Join with `&`
- const link = `qortal://use-embed/POLL?${queryParams}`;
- try {
- await navigator.clipboard.writeText(link);
- } catch (error) {
- throw new Error(
- i18n.t('question:message.error.copy_clipboard', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- return link;
- }
- case 'IMAGE':
- case 'ATTACHMENT': {
- missingFieldsFunc(data, ['type', 'name', 'service', 'identifier']);
- if (data?.encryptionType === 'private' && !data?.key) {
- throw new Error(
- i18n.t('question:message.generic.provide_key_shared_link', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const queryParams = buildQueryParams(data);
-
- const link = `qortal://use-embed/${data.type}?${queryParams}`;
-
- try {
- await navigator.clipboard.writeText(link);
- } catch (error) {
- throw new Error(
- i18n.t('question:message.error.copy_clipboard', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- return link;
- }
-
- default:
- throw new Error(
- i18n.t('question:message.error.invalid_type', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const registerNameRequest = async (data, isFromExtension) => {
- const requiredFields = ['name'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
-
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const fee = await getFee('REGISTER_NAME');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.register_name', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: data.name,
- text2: data?.description,
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const name = data.name;
- const description = data?.description || '';
- const response = await registerName({ name, description });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const updateNameRequest = async (data, isFromExtension) => {
- const requiredFields = ['newName', 'oldName'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const oldName = data.oldName;
- const newName = data.newName;
- const description = data?.description || '';
- const fee = await getFee('UPDATE_NAME');
- const resPermission = await getUserPermission(
- {
- text1: `Do you give this application permission to update this name?`,
- text2: `previous name: ${oldName}`,
- text3: `new name: ${newName}`,
- text4: data?.description,
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await updateName({ oldName, newName, description });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const leaveGroupRequest = async (data, isFromExtension) => {
- const requiredFields = ['groupId'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const groupId = data.groupId;
- let groupInfo = null;
- try {
- const url = await createEndpoint(`/groups/${groupId}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_group', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- groupInfo = await response.json();
- } catch (error) {
- const errorMsg =
- (error && error.message) ||
- i18n.t('question:message.error.no_group_found', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const fee = await getFee('LEAVE_GROUP');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.leave_group', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: `${groupInfo.groupName}`,
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await leaveGroup({ groupId });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const inviteToGroupRequest = async (data, isFromExtension) => {
- const requiredFields = ['groupId', 'inviteTime', 'inviteeAddress'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const groupId = data.groupId;
- const qortalAddress = data?.inviteeAddress;
- const inviteTime = data?.inviteTime;
-
- let groupInfo = null;
- try {
- const url = await createEndpoint(`/groups/${groupId}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_group', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- groupInfo = await response.json();
- } catch (error) {
- const errorMsg =
- (error && error.message) ||
- i18n.t('question:message.error.no_group_found', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const displayInvitee = await getNameInfoForOthers(qortalAddress);
-
- const fee = await getFee('GROUP_INVITE');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.invite', {
- invitee: displayInvitee || qortalAddress,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('group:group.group_name', {
- name: groupInfo?.groupName,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await inviteToGroup({
- groupId,
- qortalAddress,
- inviteTime,
- });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const kickFromGroupRequest = async (data, isFromExtension) => {
- const requiredFields = ['groupId', 'qortalAddress'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const groupId = data.groupId;
- const qortalAddress = data?.qortalAddress;
- const reason = data?.reason;
-
- let groupInfo = null;
- try {
- const url = await createEndpoint(`/groups/${groupId}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_group', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- groupInfo = await response.json();
- } catch (error) {
- const errorMsg =
- (error && error.message) ||
- i18n.t('question:message.error.no_group_found', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const displayInvitee = await getNameInfoForOthers(qortalAddress);
-
- const fee = await getFee('GROUP_KICK');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.kick', {
- partecipant: displayInvitee || qortalAddress,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('group:group.group_name', {
- name: groupInfo?.groupName,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await kickFromGroup({
- groupId,
- qortalAddress,
- rBanReason: reason,
- });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const banFromGroupRequest = async (data, isFromExtension) => {
- const requiredFields = ['groupId', 'qortalAddress'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const groupId = data.groupId;
- const qortalAddress = data?.qortalAddress;
- const rBanTime = data?.banTime;
- const reason = data?.reason;
- let groupInfo = null;
- try {
- const url = await createEndpoint(`/groups/${groupId}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_group', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- groupInfo = await response.json();
- } catch (error) {
- const errorMsg =
- (error && error.message) ||
- i18n.t('question:message.error.no_group_found', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const displayInvitee = await getNameInfoForOthers(qortalAddress);
-
- const fee = await getFee('GROUP_BAN');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.ban', {
- partecipant: displayInvitee || qortalAddress,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('group:group.group_name', {
- name: groupInfo?.groupName,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await banFromGroup({
- groupId,
- qortalAddress,
- rBanTime,
- rBanReason: reason,
- });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const cancelGroupBanRequest = async (data, isFromExtension) => {
- const requiredFields = ['groupId', 'qortalAddress'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const groupId = data.groupId;
- const qortalAddress = data?.qortalAddress;
-
- let groupInfo = null;
- try {
- const url = await createEndpoint(`/groups/${groupId}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_group', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- groupInfo = await response.json();
- } catch (error) {
- const errorMsg =
- (error && error.message) ||
- i18n.t('question:message.error.no_group_found', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const displayInvitee = await getNameInfoForOthers(qortalAddress);
-
- const fee = await getFee('CANCEL_GROUP_BAN');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.cancel_ban', {
- partecipant: displayInvitee || qortalAddress,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('group:group.group_name', {
- name: groupInfo?.groupName,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await cancelBan({
- groupId,
- qortalAddress,
- });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const addGroupAdminRequest = async (data, isFromExtension) => {
- const requiredFields = ['groupId', 'qortalAddress'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const groupId = data.groupId;
- const qortalAddress = data?.qortalAddress;
-
- let groupInfo = null;
- try {
- const url = await createEndpoint(`/groups/${groupId}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_group', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- groupInfo = await response.json();
- } catch (error) {
- const errorMsg =
- (error && error.message) ||
- i18n.t('question:message.error.no_group_found', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const displayInvitee = await getNameInfoForOthers(qortalAddress);
-
- const fee = await getFee('ADD_GROUP_ADMIN');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.add_admin', {
- invitee: displayInvitee || qortalAddress,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('group:group.group_name', {
- name: groupInfo?.groupName,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await makeAdmin({
- groupId,
- qortalAddress,
- });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const removeGroupAdminRequest = async (data, isFromExtension) => {
- const requiredFields = ['groupId', 'qortalAddress'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const groupId = data.groupId;
- const qortalAddress = data?.qortalAddress;
-
- let groupInfo = null;
- try {
- const url = await createEndpoint(`/groups/${groupId}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_group', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- groupInfo = await response.json();
- } catch (error) {
- const errorMsg =
- (error && error.message) ||
- i18n.t('question:message.error.no_group_found', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const displayInvitee = await getNameInfoForOthers(qortalAddress);
-
- const fee = await getFee('REMOVE_GROUP_ADMIN');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.remove_admin', {
- partecipant: displayInvitee || qortalAddress,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('group:group.group_name', {
- name: groupInfo?.groupName,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await removeAdmin({
- groupId,
- qortalAddress,
- });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const cancelGroupInviteRequest = async (data, isFromExtension) => {
- const requiredFields = ['groupId', 'qortalAddress'];
- const missingFields: string[] = [];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- missingFields.push(field);
- }
- });
- if (missingFields.length > 0) {
- const missingFieldsString = missingFields.join(', ');
- const errorMsg = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const groupId = data.groupId;
- const qortalAddress = data?.qortalAddress;
-
- let groupInfo = null;
- try {
- const url = await createEndpoint(`/groups/${groupId}`);
- const response = await fetch(url);
- if (!response.ok)
- throw new Error(
- i18n.t('question:message.error.fetch_group', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- groupInfo = await response.json();
- } catch (error) {
- const errorMsg =
- (error && error.message) ||
- i18n.t('question:message.error.no_group_found', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const displayInvitee = await getNameInfoForOthers(qortalAddress);
-
- const fee = await getFee('CANCEL_GROUP_INVITE');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.cancel_group_invite', {
- invitee: displayInvitee || qortalAddress,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('group:group.group_name', {
- name: groupInfo?.groupName,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
-
- const { accepted } = resPermission;
-
- if (accepted) {
- const response = await cancelInvitationToGroup({
- groupId,
- qortalAddress,
- });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const createGroupRequest = async (data, isFromExtension) => {
- const requiredFields = [
- 'approvalThreshold',
- 'groupId',
- 'groupName',
- 'maxBlock',
- 'minBlock',
- 'qortalAddress',
- 'type',
- ];
- 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 = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
- const groupName = data.groupName;
- const description = data?.description || '';
- const type = +data.type;
- const approvalThreshold = +data?.approvalThreshold;
- const minBlock = +data?.minBlock;
- const maxBlock = +data.maxBlock;
-
- const fee = await getFee('CREATE_GROUP');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.create_group', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('group:group.group_name', {
- name: groupName,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await createGroup({
- groupName,
- groupDescription: description,
- groupType: type,
- groupApprovalThreshold: approvalThreshold,
- minBlock,
- maxBlock,
- });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-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 = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- 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(
- i18n.t('question:message.error.fetch_group', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- groupInfo = await response.json();
- } catch (error) {
- const errorMsg =
- (error && error.message) ||
- i18n.t('question:message.error.no_group_found', {
- postProcess: 'capitalizeFirstChar',
- });
- throw new Error(errorMsg);
- }
-
- const displayInvitee = await getNameInfoForOthers(newOwner);
-
- const fee = await getFee('CREATE_GROUP');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.update_group', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:permission.update_group_detail', {
- owner: displayInvitee || newOwner,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('group:group.group_name', {
- name: groupInfo?.groupName,
- postProcess: 'capitalizeFirstChar',
- }),
- 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(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const decryptAESGCMRequest = async (data, isFromExtension) => {
- const requiredFields = ['encryptedData', 'iv', 'senderPublicKey'];
- requiredFields.forEach((field) => {
- if (!data[field]) {
- throw new Error(
- i18n.t('question:message.error.missing_fields', {
- fields: field,
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- });
-
- const encryptedData = data.encryptedData;
- const iv = data.iv;
- const senderPublicKeyBase58 = data.senderPublicKey;
-
- // Decode keys and IV
- const senderPublicKey = Base58.decode(senderPublicKeyBase58);
- const resKeyPair = await getKeyPair(); // Assume this retrieves the current user's keypair
- const uint8PrivateKey = Base58.decode(resKeyPair.privateKey);
-
- // Convert ed25519 keys to Curve25519
- const convertedPrivateKey = ed2curve.convertSecretKey(uint8PrivateKey);
- const convertedPublicKey = ed2curve.convertPublicKey(senderPublicKey);
-
- // Generate shared secret
- const sharedSecret = new Uint8Array(32);
- nacl.lowlevel.crypto_scalarmult(
- sharedSecret,
- convertedPrivateKey,
- convertedPublicKey
- );
-
- // Derive encryption key
- const encryptionKey: Uint8Array = new Sha256()
- .process(sharedSecret)
- .finish().result;
-
- // Convert IV and ciphertext from Base64
- const base64ToUint8Array = (base64) =>
- Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
- const ivUint8Array = base64ToUint8Array(iv);
- const ciphertext = base64ToUint8Array(encryptedData);
- // Validate IV and key lengths
- if (ivUint8Array.length !== 12) {
- throw new Error(
- i18n.t('question:message.error.invalid_encryption_iv', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- if (encryptionKey.length !== 32) {
- throw new Error(
- i18n.t('question:message.error.invalid_encryption_key', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- try {
- // Decrypt data
- const algorithm = { name: 'AES-GCM', iv: ivUint8Array };
- const cryptoKey = await crypto.subtle.importKey(
- 'raw',
- encryptionKey,
- algorithm,
- false,
- ['decrypt']
- );
- const decryptedArrayBuffer = await crypto.subtle.decrypt(
- algorithm,
- cryptoKey,
- ciphertext
- );
-
- // Return decrypted data as Base64
- return uint8ArrayToBase64(new Uint8Array(decryptedArrayBuffer));
- } catch (error) {
- console.error('Decryption failed:', error);
- throw new Error(
- i18n.t('question:message.error.decrypt_message', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-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 = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- 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(
- i18n.t('auth:message.error.name_not_existing', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- if (nameData?.isForSale)
- throw new Error(
- i18n.t('question:message.error.name_already_for_sale', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const fee = await getFee('SELL_NAME');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.sell_name_transaction', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t(
- 'question:permission.sell_name_transaction_detail',
- {
- name: name,
- price: sellPrice,
- postProcess: 'capitalizeFirstChar',
- }
- ),
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await sellName({
- name,
- sellPrice,
- });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-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 = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- 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(
- i18n.t('question:message.error.name_not_for_sale', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- const fee = await getFee('CANCEL_SELL_NAME');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.sell_name_cancel', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('question:name', {
- name: name,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await cancelSellName({
- name,
- });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-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 = i18n.t('question:message.error.missing_fields', {
- fields: missingFieldsString,
- postProcess: 'capitalizeFirstChar',
- });
- 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(
- i18n.t('question:message.error.name_not_for_sale', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- const sellerAddress = nameData.owner;
- const sellPrice = +nameData.salePrice;
-
- const fee = await getFee('BUY_NAME');
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.buy_name', {
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('question:permission.buy_name_detail', {
- name: name,
- price: sellPrice,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee.fee,
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const response = await buyName({
- name,
- sellerAddress,
- sellPrice,
- });
- return response;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-
-export const signForeignFees = async (data, isFromExtension) => {
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.sign_fee', {
- postProcess: 'capitalizeFirstChar',
- }),
- },
- isFromExtension
- );
- const { accepted } = resPermission;
- if (accepted) {
- const wallet = await getSaveWallet();
- const address = wallet.address0;
- 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 unsignedFeesUrl = await createEndpoint(
- `/crosschain/unsignedfees/${address}`
- );
-
- const unsignedFeesResponse = await fetch(unsignedFeesUrl);
-
- const unsignedFees = await unsignedFeesResponse.json();
-
- const signedFees = [];
-
- unsignedFees.forEach((unsignedFee) => {
- const unsignedDataDecoded = Base58.decode(unsignedFee.data);
-
- const signature = nacl.sign.detached(
- unsignedDataDecoded,
- keyPair.privateKey
- );
-
- const signedFee = {
- timestamp: unsignedFee.timestamp,
- data: `${Base58.encode(signature)}`,
- atAddress: unsignedFee.atAddress,
- fee: unsignedFee.fee,
- };
-
- signedFees.push(signedFee);
- });
-
- const signedFeesUrl = await createEndpoint(`/crosschain/signedfees`);
-
- await fetch(signedFeesUrl, {
- method: 'POST',
- headers: {
- Accept: '*/*',
- 'Content-Type': 'application/json',
- },
- body: `${JSON.stringify(signedFees)}`,
- });
-
- return true;
- } else {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-};
-export const multiPaymentWithPrivateData = async (data, isFromExtension) => {
- const requiredFields = ['payments', 'assetId'];
- requiredFields.forEach((field) => {
- if (data[field] === undefined || data[field] === null) {
- throw new Error(
- i18n.t('question:message.error.missing_fields', {
- fields: field,
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- });
- const resKeyPair = await getKeyPair();
- const parsedData = resKeyPair;
- const privateKey = parsedData.privateKey;
- const userPublicKey = parsedData.publicKey;
- const { fee: paymentFee } = await getFee('TRANSFER_ASSET');
- const { fee: arbitraryFee } = await getFee('ARBITRARY');
-
- let name = null;
- const payments = data.payments;
- const assetId = data.assetId;
- const pendingTransactions = [];
- const pendingAdditionalArbitraryTxs = [];
- const additionalArbitraryTxsWithoutPayment =
- data?.additionalArbitraryTxsWithoutPayment || [];
- let totalAmount = 0;
- let fee = 0;
- for (const payment of payments) {
- const paymentRefId = uid.rnd();
- const requiredFieldsPayment = ['recipient', 'amount'];
-
- for (const field of requiredFieldsPayment) {
- if (!payment[field]) {
- throw new Error(
- i18n.t('question:message.error.missing_fields', {
- fields: field,
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- }
-
- const confirmReceiver = await getNameOrAddress(payment.recipient);
- if (confirmReceiver.error) {
- throw new Error(
- i18n.t('question:message.error.invalid_receiver', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const receiverPublicKey = await getPublicKey(confirmReceiver);
-
- const amount = +payment.amount.toFixed(8);
-
- pendingTransactions.push({
- type: 'PAYMENT',
- recipientAddress: confirmReceiver,
- amount: amount,
- paymentRefId,
- });
-
- fee = fee + +paymentFee;
- totalAmount = totalAmount + amount;
-
- if (payment.arbitraryTxs && payment.arbitraryTxs.length > 0) {
- for (const arbitraryTx of payment.arbitraryTxs) {
- const requiredFieldsArbitraryTx = ['service', 'identifier', 'base64'];
-
- for (const field of requiredFieldsArbitraryTx) {
- if (!arbitraryTx[field]) {
- throw new Error(
- i18n.t('question:message.error.missing_fields', {
- fields: field,
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- }
-
- if (!name) {
- const getName = await getNameInfo();
- if (!getName)
- throw new Error(
- i18n.t('question:message.error.registered_name', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- name = getName;
- }
-
- const isValid = isValidBase64WithDecode(arbitraryTx.base64);
- if (!isValid)
- throw new Error(
- i18n.t('core:message.error.invalid_base64', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- if (!arbitraryTx?.service?.includes('_PRIVATE'))
- throw new Error(
- i18n.t('question:message.generic.private_service', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const additionalPublicKeys = arbitraryTx?.additionalPublicKeys || [];
- pendingTransactions.push({
- type: 'ARBITRARY',
- identifier: arbitraryTx.identifier,
- service: arbitraryTx.service,
- base64: arbitraryTx.base64,
- description: arbitraryTx?.description || '',
- paymentRefId,
- publicKeys: [receiverPublicKey, ...additionalPublicKeys],
- });
-
- fee = fee + +arbitraryFee;
- }
- }
- }
-
- if (
- additionalArbitraryTxsWithoutPayment &&
- additionalArbitraryTxsWithoutPayment.length > 0
- ) {
- for (const arbitraryTx of additionalArbitraryTxsWithoutPayment) {
- const requiredFieldsArbitraryTx = ['service', 'identifier', 'base64'];
-
- for (const field of requiredFieldsArbitraryTx) {
- if (!arbitraryTx[field]) {
- throw new Error(
- i18n.t('question:message.error.missing_fields', {
- fields: field,
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- }
-
- if (!name) {
- const getName = await getNameInfo();
- if (!getName)
- throw new Error(
- i18n.t('question:message.error.registered_name', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- name = getName;
- }
-
- const isValid = isValidBase64WithDecode(arbitraryTx.base64);
- if (!isValid)
- throw new Error(
- i18n.t('core:message.error.invalid_base64', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- if (!arbitraryTx?.service?.includes('_PRIVATE'))
- throw new Error(
- i18n.t('question:message.generic.private_service', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const additionalPublicKeys = arbitraryTx?.additionalPublicKeys || [];
- pendingAdditionalArbitraryTxs.push({
- type: 'ARBITRARY',
- identifier: arbitraryTx.identifier,
- service: arbitraryTx.service,
- base64: arbitraryTx.base64,
- description: arbitraryTx?.description || '',
- publicKeys: additionalPublicKeys,
- });
-
- fee = fee + +arbitraryFee;
- }
- }
-
- if (!name)
- throw new Error(
- i18n.t('question:message.error.registered_name', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const balance = await getBalanceInfo();
-
- if (+balance < fee)
- throw new Error(
- i18n.t('question:message.error.insufficient_balance_qort', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const assetBalance = await getAssetBalanceInfo(assetId);
- const assetInfo = await getAssetInfo(assetId);
- if (assetBalance < totalAmount)
- throw new Error(
- i18n.t('question:message.error.insufficient_balance', {
- postProcess: 'capitalizeFirstChar',
- })
- );
-
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.pay_publish', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:assets_used_pay', {
- asset: assetInfo.name,
- postProcess: 'capitalizeFirstChar',
- }),
- html: `
-
-
-
- ${pendingTransactions
- .filter((item) => item.type === 'PAYMENT')
- .map(
- (payment) => `
-
-
Recipient: ${
- payment.recipientAddress
- }
-
Amount: ${payment.amount}
-
`
- )
- .join('')}
- ${[...pendingTransactions, ...pendingAdditionalArbitraryTxs]
- .filter((item) => item.type === 'ARBITRARY')
- .map(
- (arbitraryTx) => `
-
-
Service: ${
- arbitraryTx.service
- }
-
Name: ${name}
-
Identifier: ${
- arbitraryTx.identifier
- }
-
`
- )
- .join('')}
-
-
- `,
- highlightedText: `Total Amount: ${totalAmount}`,
- fee: fee,
- },
- isFromExtension
- );
-
- const { accepted, checkbox1 = false } = resPermission;
- if (!accepted) {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
-
- // const failedTxs = []
- const paymentsDone = {};
-
- const transactionsDone = [];
-
- for (const transaction of pendingTransactions) {
- const type = transaction.type;
-
- if (type === 'PAYMENT') {
- const makePayment = await retryTransaction(
- transferAsset,
- [
- {
- amount: transaction.amount,
- assetId,
- recipient: transaction.recipientAddress,
- },
- ],
- true
- );
- if (makePayment) {
- transactionsDone.push(makePayment?.signature);
- if (transaction.paymentRefId) {
- paymentsDone[transaction.paymentRefId] = makePayment;
- }
- }
- } else if (type === 'ARBITRARY' && paymentsDone[transaction.paymentRefId]) {
- const objectToEncrypt = {
- data: transaction.base64,
- payment: paymentsDone[transaction.paymentRefId],
- };
-
- const toBase64 = await retryTransaction(
- objectToBase64,
- [objectToEncrypt],
- true
- );
-
- if (!toBase64) continue; // Skip if encryption fails
-
- const encryptDataResponse = await retryTransaction(
- encryptDataGroup,
- [
- {
- data64: toBase64,
- publicKeys: transaction.publicKeys,
- privateKey,
- userPublicKey,
- },
- ],
- true
- );
-
- if (!encryptDataResponse) continue; // Skip if encryption fails
-
- const resPublish = await retryTransaction(
- publishData,
- [
- {
- registeredName: encodeURIComponent(name),
- data: encryptDataResponse,
- service: transaction.service,
- identifier: encodeURIComponent(transaction.identifier),
- uploadType: 'base64',
- description: transaction?.description,
- apiVersion: 2,
- withFee: true,
- },
- ],
- true
- );
-
- if (resPublish?.signature) {
- transactionsDone.push(resPublish?.signature);
- }
- }
- }
-
- for (const transaction of pendingAdditionalArbitraryTxs) {
- const objectToEncrypt = {
- data: transaction.base64,
- };
-
- const toBase64 = await retryTransaction(
- objectToBase64,
- [objectToEncrypt],
- true
- );
-
- if (!toBase64) continue; // Skip if encryption fails
-
- const encryptDataResponse = await retryTransaction(
- encryptDataGroup,
- [
- {
- data64: toBase64,
- publicKeys: transaction.publicKeys,
- privateKey,
- userPublicKey,
- },
- ],
- true
- );
-
- if (!encryptDataResponse) continue; // Skip if encryption fails
-
- const resPublish = await retryTransaction(
- publishData,
- [
- {
- registeredName: encodeURIComponent(name),
- data: encryptDataResponse,
- service: transaction.service,
- identifier: encodeURIComponent(transaction.identifier),
- uploadType: 'base64',
- description: transaction?.description,
- apiVersion: 2,
- withFee: true,
- },
- ],
- true
- );
-
- if (resPublish?.signature) {
- transactionsDone.push(resPublish?.signature);
- }
- }
-
- return transactionsDone;
-};
-
-export const transferAssetRequest = async (data, isFromExtension) => {
- const requiredFields = ['amount', 'assetId', 'recipient'];
- requiredFields.forEach((field) => {
- if (data[field] === undefined || data[field] === null) {
- throw new Error(
- i18n.t('question:message.error.missing_fields', {
- fields: field,
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- });
- const amount = data.amount;
- const assetId = data.assetId;
- const recipient = data.recipient;
-
- const { fee } = await getFee('TRANSFER_ASSET');
- const balance = await getBalanceInfo();
-
- if (+balance < +fee)
- throw new Error(
- i18n.t('question:message.error.insufficient_balance_qort', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const assetBalance = await getAssetBalanceInfo(assetId);
- if (assetBalance < amount)
- throw new Error(
- i18n.t('question:message.error.insufficient_balance', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- const confirmReceiver = await getNameOrAddress(recipient);
- if (confirmReceiver.error) {
- throw new Error(
- i18n.t('question:message.error.invalid_receiver', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const assetInfo = await getAssetInfo(assetId);
- const resPermission = await getUserPermission(
- {
- text1: i18n.t('question:permission.transfer_asset', {
- postProcess: 'capitalizeFirstChar',
- }),
- text2: i18n.t('question:asset_name', {
- asset: assetInfo?.name,
- postProcess: 'capitalizeFirstChar',
- }),
- highlightedText: i18n.t('question:amount_qty', {
- quantity: amount,
- postProcess: 'capitalizeFirstChar',
- }),
- fee: fee,
- },
- isFromExtension
- );
-
- const { accepted } = resPermission;
- if (!accepted) {
- throw new Error(
- i18n.t('question:message.generic.user_declined_request', {
- postProcess: 'capitalizeFirstChar',
- })
- );
- }
- const res = await transferAsset({
- amount,
- recipient: confirmReceiver,
- assetId,
- });
- return res;
-};