mirror of
https://github.com/Qortal/chrome-extension.git
synced 2025-03-28 08:15:55 +00:00
added trade qortalRequests
This commit is contained in:
parent
ceeac9aac7
commit
d2f1a0f9c9
30
src/App.tsx
30
src/App.tsx
@ -628,7 +628,9 @@ function App() {
|
|||||||
const qortalRequestPermissonFromExtension = async (message, sender, sendResponse) => {
|
const qortalRequestPermissonFromExtension = async (message, sender, sendResponse) => {
|
||||||
if (message.action === "QORTAL_REQUEST_PERMISSION" && isMainWindow) {
|
if (message.action === "QORTAL_REQUEST_PERMISSION" && isMainWindow) {
|
||||||
try {
|
try {
|
||||||
|
if(message?.payload?.checkbox1){
|
||||||
|
qortalRequestCheckbox1Ref.current = message?.payload?.checkbox1
|
||||||
|
}
|
||||||
await showQortalRequestExtension(message?.payload);
|
await showQortalRequestExtension(message?.payload);
|
||||||
|
|
||||||
if (qortalRequestCheckbox1Ref.current) {
|
if (qortalRequestCheckbox1Ref.current) {
|
||||||
@ -1069,7 +1071,7 @@ function App() {
|
|||||||
resetAllRecoil()
|
resetAllRecoil()
|
||||||
};
|
};
|
||||||
|
|
||||||
function roundUpToDecimals(number, decimals = 8) {
|
function roundUpToDecimals(number, decimals = 8) {
|
||||||
const factor = Math.pow(10, decimals); // Create a factor based on the number of decimals
|
const factor = Math.pow(10, decimals); // Create a factor based on the number of decimals
|
||||||
return Math.ceil(+number * factor) / factor;
|
return Math.ceil(+number * factor) / factor;
|
||||||
}
|
}
|
||||||
@ -1463,10 +1465,11 @@ function App() {
|
|||||||
textDecoration: "underline",
|
textDecoration: "underline",
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
chrome.tabs.create({ url: "https://www.qort.trade" });
|
executeEvent("addTab", { data: { service: 'APP', name: 'q-trade' } });
|
||||||
|
executeEvent("open-apps-mode", { });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Get QORT at qort.trade
|
Get QORT at Q-Trade
|
||||||
</TextP>
|
</TextP>
|
||||||
</AuthenticatedContainerInnerLeft>
|
</AuthenticatedContainerInnerLeft>
|
||||||
<AuthenticatedContainerInnerRight>
|
<AuthenticatedContainerInnerRight>
|
||||||
@ -2769,6 +2772,25 @@ function App() {
|
|||||||
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{messageQortalRequestExtension?.foreignFee && (
|
||||||
|
<>
|
||||||
|
<Spacer height="15px" />
|
||||||
|
|
||||||
|
<TextP
|
||||||
|
sx={{
|
||||||
|
textAlign: "center",
|
||||||
|
lineHeight: 1.2,
|
||||||
|
fontSize: "16px",
|
||||||
|
fontWeight: "normal",
|
||||||
|
maxWidth: "90%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{"Foreign Fee: "}
|
||||||
|
{messageQortalRequestExtension?.foreignFee}
|
||||||
|
</TextP>
|
||||||
|
<Spacer height="15px" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{messageQortalRequestExtension?.checkbox1 && (
|
{messageQortalRequestExtension?.checkbox1 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -24,6 +24,10 @@ export const sortablePinnedAppsAtom = atom({
|
|||||||
},{
|
},{
|
||||||
name: 'Qombo',
|
name: 'Qombo',
|
||||||
service: 'APP'
|
service: 'APP'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Q-Trade',
|
||||||
|
service: 'APP'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -44,6 +44,9 @@ export function getProtocol(url) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const gateways = ['ext-node.qortal.link']
|
||||||
|
|
||||||
|
|
||||||
let lastGroupNotification;
|
let lastGroupNotification;
|
||||||
export const groupApi = "https://ext-node.qortal.link";
|
export const groupApi = "https://ext-node.qortal.link";
|
||||||
export const groupApiSocket = "wss://ext-node.qortal.link";
|
export const groupApiSocket = "wss://ext-node.qortal.link";
|
||||||
@ -102,6 +105,18 @@ export const clearAllQueues = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getForeignKey = async (foreignBlockchain)=> {
|
||||||
|
const resKeyPair = await getKeyPair();
|
||||||
|
const parsedData = JSON.parse(resKeyPair);
|
||||||
|
switch (foreignBlockchain) {
|
||||||
|
case "LITECOIN":
|
||||||
|
return parsedData.ltcPrivateKey
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const pauseAllQueues = () => controlAllQueues("pause");
|
const pauseAllQueues = () => controlAllQueues("pause");
|
||||||
const resumeAllQueues = () => controlAllQueues("resume");
|
const resumeAllQueues = () => controlAllQueues("resume");
|
||||||
const checkDifference = (createdTimestamp) => {
|
const checkDifference = (createdTimestamp) => {
|
||||||
@ -109,7 +124,7 @@ const checkDifference = (createdTimestamp) => {
|
|||||||
Date.now() - createdTimestamp < timeDifferenceForNotificationChatsBackground
|
Date.now() - createdTimestamp < timeDifferenceForNotificationChatsBackground
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const getApiKeyFromStorage = async () => {
|
export const getApiKeyFromStorage = async () => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
chrome.storage.local.get("apiKey", (result) => {
|
chrome.storage.local.get("apiKey", (result) => {
|
||||||
if (chrome.runtime.lastError) {
|
if (chrome.runtime.lastError) {
|
||||||
@ -172,6 +187,7 @@ export const isUsingLocal = async () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const createEndpoint = async (endpoint, customApi?: string) => {
|
export const createEndpoint = async (endpoint, customApi?: string) => {
|
||||||
if (customApi) {
|
if (customApi) {
|
||||||
return `${customApi}${endpoint}`;
|
return `${customApi}${endpoint}`;
|
||||||
@ -1255,6 +1271,69 @@ async function getDataPublishes(groupId, type) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendChatForBuyOrder({ qortAddress, recipientPublicKey, message }) {
|
||||||
|
console.log('test3', qortAddress, recipientPublicKey, message)
|
||||||
|
let _reference = new Uint8Array(64);
|
||||||
|
self.crypto.getRandomValues(_reference);
|
||||||
|
|
||||||
|
let sendTimestamp = Date.now();
|
||||||
|
|
||||||
|
let reference = Base58.encode(_reference);
|
||||||
|
const resKeyPair = await getKeyPair();
|
||||||
|
const parsedData = JSON.parse(resKeyPair);
|
||||||
|
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
|
||||||
|
const uint8PublicKey = Base58.decode(parsedData.publicKey);
|
||||||
|
const keyPair = {
|
||||||
|
privateKey: uint8PrivateKey,
|
||||||
|
publicKey: uint8PublicKey,
|
||||||
|
};
|
||||||
|
const balance = await getBalanceInfo();
|
||||||
|
const hasEnoughBalance = +balance < 4 ? false : true;
|
||||||
|
const difficulty = 8;
|
||||||
|
const jsonData = {
|
||||||
|
addresses: message.addresses,
|
||||||
|
foreignKey: message.foreignKey,
|
||||||
|
receivingAddress: message.receivingAddress,
|
||||||
|
};
|
||||||
|
const finalJson = {
|
||||||
|
callRequest: jsonData,
|
||||||
|
extra: "whatever additional data goes here",
|
||||||
|
};
|
||||||
|
const messageStringified = JSON.stringify(finalJson);
|
||||||
|
|
||||||
|
const tx = await createTransaction(18, keyPair, {
|
||||||
|
timestamp: sendTimestamp,
|
||||||
|
recipient: qortAddress,
|
||||||
|
recipientPublicKey: recipientPublicKey,
|
||||||
|
hasChatReference: 0,
|
||||||
|
message: messageStringified,
|
||||||
|
lastReference: reference,
|
||||||
|
proofOfWorkNonce: 0,
|
||||||
|
isEncrypted: 1,
|
||||||
|
isText: 1,
|
||||||
|
});
|
||||||
|
if (!hasEnoughBalance) {
|
||||||
|
throw new Error('You must have at least 4 QORT to trade using the gateway.')
|
||||||
|
}
|
||||||
|
const path = `${import.meta.env.BASE_URL}memory-pow.wasm.full`;
|
||||||
|
|
||||||
|
const { nonce, chatBytesArray } = await computePow({
|
||||||
|
chatBytes: tx.chatBytes,
|
||||||
|
path,
|
||||||
|
difficulty,
|
||||||
|
});
|
||||||
|
let _response = await signChatFunc(
|
||||||
|
chatBytesArray,
|
||||||
|
nonce,
|
||||||
|
"https://appnode.qortal.org",
|
||||||
|
keyPair
|
||||||
|
);
|
||||||
|
if (_response?.error) {
|
||||||
|
throw new Error(_response?.message);
|
||||||
|
}
|
||||||
|
return _response;
|
||||||
|
}
|
||||||
|
|
||||||
async function addDataPublishes(newData, groupId, type) {
|
async function addDataPublishes(newData, groupId, type) {
|
||||||
const wallet = await getSaveWallet();
|
const wallet = await getSaveWallet();
|
||||||
const address = wallet.address0;
|
const address = wallet.address0;
|
||||||
@ -1931,6 +2010,120 @@ async function createBuyOrderTx({ crosschainAtInfo, useLocal }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function createBuyOrderTxQortalRequest({ crosschainAtInfo, isGateway, foreignBlockchain }) {
|
||||||
|
try {
|
||||||
|
console.log('test2', crosschainAtInfo, isGateway, foreignBlockchain)
|
||||||
|
if (!isGateway) {
|
||||||
|
const wallet = await getSaveWallet();
|
||||||
|
|
||||||
|
const address = wallet.address0;
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
addresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
|
||||||
|
foreignKey: await getForeignKey(foreignBlockchain),
|
||||||
|
receivingAddress: address,
|
||||||
|
};
|
||||||
|
let responseVar;
|
||||||
|
const txn = new TradeBotRespondMultipleRequest().createTransaction(
|
||||||
|
message
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const url = await createEndpoint('/crosschain/tradebot/respondmultiple')
|
||||||
|
|
||||||
|
const responseFetch = await fetch(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(txn),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const res = await responseFetch.json();
|
||||||
|
|
||||||
|
if (res === false) {
|
||||||
|
responseVar = {
|
||||||
|
response: "Unable to execute buy order",
|
||||||
|
success: false,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
responseVar = { response: res, success: true };
|
||||||
|
}
|
||||||
|
const { response, success } = responseVar;
|
||||||
|
let responseMessage;
|
||||||
|
if (success) {
|
||||||
|
responseMessage = {
|
||||||
|
callResponse: response,
|
||||||
|
extra: {
|
||||||
|
message: "Transaction processed successfully!",
|
||||||
|
atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
|
||||||
|
senderAddress: address,
|
||||||
|
node: url
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
responseMessage = {
|
||||||
|
callResponse: "ERROR",
|
||||||
|
extra: {
|
||||||
|
message: response,
|
||||||
|
atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
|
||||||
|
senderAddress: address,
|
||||||
|
node: url
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseMessage
|
||||||
|
}
|
||||||
|
const wallet = await getSaveWallet();
|
||||||
|
const address = wallet.address0;
|
||||||
|
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
addresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
|
||||||
|
foreignKey: await getForeignKey(foreignBlockchain),
|
||||||
|
receivingAddress: address,
|
||||||
|
};
|
||||||
|
const res = await sendChatForBuyOrder({
|
||||||
|
qortAddress: proxyAccountAddress,
|
||||||
|
recipientPublicKey: proxyAccountPublicKey,
|
||||||
|
message,
|
||||||
|
atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (res?.signature) {
|
||||||
|
|
||||||
|
const message = await listenForChatMessageForBuyOrderQortalRequest({
|
||||||
|
nodeBaseUrl: buyTradeNodeBaseUrl,
|
||||||
|
senderAddress: proxyAccountAddress,
|
||||||
|
senderPublicKey: proxyAccountPublicKey,
|
||||||
|
signature: res?.signature,
|
||||||
|
});
|
||||||
|
|
||||||
|
const responseMessage = {
|
||||||
|
callResponse: message.callResponse,
|
||||||
|
extra: {
|
||||||
|
message: message?.extra?.message,
|
||||||
|
senderAddress: address,
|
||||||
|
node: buyTradeNodeBaseUrl,
|
||||||
|
atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseMessage
|
||||||
|
} else {
|
||||||
|
throw new Error("Unable to send buy order message");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function sendChatNotification(
|
async function sendChatNotification(
|
||||||
res,
|
res,
|
||||||
groupId,
|
groupId,
|
||||||
@ -2544,6 +2737,40 @@ async function listenForChatMessageForBuyOrder({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function listenForChatMessageForBuyOrderQortalRequest({
|
||||||
|
nodeBaseUrl,
|
||||||
|
senderAddress,
|
||||||
|
senderPublicKey,
|
||||||
|
signature,
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
let validApi = "";
|
||||||
|
const checkIfNodeBaseUrlIsAcceptable = apiEndpoints.find(
|
||||||
|
(item) => item === nodeBaseUrl
|
||||||
|
);
|
||||||
|
if (checkIfNodeBaseUrlIsAcceptable) {
|
||||||
|
validApi = checkIfNodeBaseUrlIsAcceptable;
|
||||||
|
} else {
|
||||||
|
validApi = await findUsableApi();
|
||||||
|
}
|
||||||
|
const wallet = await getSaveWallet();
|
||||||
|
const address = wallet.address0;
|
||||||
|
const before = Date.now() + 1200000;
|
||||||
|
const after = Date.now();
|
||||||
|
const apiCall = `${validApi}/chat/messages?involving=${senderAddress}&involving=${address}&reverse=true&limit=1&before=${before}&after=${after}&encoding=BASE64`;
|
||||||
|
const parsedMessageObj = await fetchMessagesForBuyOrders(
|
||||||
|
apiCall,
|
||||||
|
signature,
|
||||||
|
senderPublicKey
|
||||||
|
);
|
||||||
|
|
||||||
|
return parsedMessageObj
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
throw new Error(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function removeDuplicateWindow(popupUrl) {
|
export function removeDuplicateWindow(popupUrl) {
|
||||||
chrome.windows.getAll(
|
chrome.windows.getAll(
|
||||||
{ populate: true, windowTypes: ["popup"] },
|
{ populate: true, windowTypes: ["popup"] },
|
||||||
|
@ -106,9 +106,18 @@ export const Apps = ({ mode, setMode, show , myName}) => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getQapps();
|
|
||||||
getCategories()
|
getCategories()
|
||||||
}, [getQapps, getCategories]);
|
}, [getCategories]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getQapps();
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
getQapps();
|
||||||
|
}, 20 * 60 * 1000); // 20 minutes in milliseconds
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [getQapps]);
|
||||||
|
|
||||||
const selectedAppInfoFunc = (e) => {
|
const selectedAppInfoFunc = (e) => {
|
||||||
const data = e.detail?.data;
|
const data = e.detail?.data;
|
||||||
@ -292,6 +301,7 @@ export const Apps = ({ mode, setMode, show , myName}) => {
|
|||||||
myName={myName}
|
myName={myName}
|
||||||
hasPublishApp={!!(myApp || myWebsite)}
|
hasPublishApp={!!(myApp || myWebsite)}
|
||||||
categories={categories}
|
categories={categories}
|
||||||
|
getQapps={getQapps}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{mode === "appInfo" && !selectedTab && <AppInfo app={selectedAppInfo} myName={myName} />}
|
{mode === "appInfo" && !selectedTab && <AppInfo app={selectedAppInfo} myName={myName} />}
|
||||||
|
@ -30,16 +30,7 @@ import { Spacer } from "../../common/Spacer";
|
|||||||
import { AppInfoSnippet } from "./AppInfoSnippet";
|
import { AppInfoSnippet } from "./AppInfoSnippet";
|
||||||
import { Virtuoso } from "react-virtuoso";
|
import { Virtuoso } from "react-virtuoso";
|
||||||
import { executeEvent } from "../../utils/events";
|
import { executeEvent } from "../../utils/events";
|
||||||
const officialAppList = [
|
|
||||||
"q-tube",
|
|
||||||
"q-blog",
|
|
||||||
"q-share",
|
|
||||||
"q-support",
|
|
||||||
"q-mail",
|
|
||||||
"qombo",
|
|
||||||
"q-fund",
|
|
||||||
"q-shop",
|
|
||||||
];
|
|
||||||
|
|
||||||
const ScrollerStyled = styled('div')({
|
const ScrollerStyled = styled('div')({
|
||||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||||
|
@ -38,16 +38,7 @@ import { AppInfoSnippet } from "./AppInfoSnippet";
|
|||||||
import { Virtuoso } from "react-virtuoso";
|
import { Virtuoso } from "react-virtuoso";
|
||||||
import { executeEvent } from "../../utils/events";
|
import { executeEvent } from "../../utils/events";
|
||||||
import { AppsDesktopLibraryBody, AppsDesktopLibraryHeader } from "./AppsDesktop-styles";
|
import { AppsDesktopLibraryBody, AppsDesktopLibraryHeader } from "./AppsDesktop-styles";
|
||||||
const officialAppList = [
|
|
||||||
"q-tube",
|
|
||||||
"q-blog",
|
|
||||||
"q-share",
|
|
||||||
"q-support",
|
|
||||||
"q-mail",
|
|
||||||
"qombo",
|
|
||||||
"q-fund",
|
|
||||||
"q-shop",
|
|
||||||
];
|
|
||||||
|
|
||||||
const ScrollerStyled = styled("div")({
|
const ScrollerStyled = styled("div")({
|
||||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||||
|
@ -109,10 +109,20 @@ export const AppsDesktop = ({ mode, setMode, show , myName, goToHome, setDesktop
|
|||||||
// dispatch(setIsLoadingGlobal(false))
|
// dispatch(setIsLoadingGlobal(false))
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getQapps();
|
|
||||||
getCategories()
|
getCategories()
|
||||||
}, [getQapps, getCategories]);
|
}, [getCategories]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getQapps();
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
getQapps();
|
||||||
|
}, 20 * 60 * 1000); // 20 minutes in milliseconds
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [getQapps]);
|
||||||
|
|
||||||
const selectedAppInfoFunc = (e) => {
|
const selectedAppInfoFunc = (e) => {
|
||||||
const data = e.detail?.data;
|
const data = e.detail?.data;
|
||||||
@ -386,6 +396,7 @@ export const AppsDesktop = ({ mode, setMode, show , myName, goToHome, setDesktop
|
|||||||
myName={myName}
|
myName={myName}
|
||||||
hasPublishApp={!!(myApp || myWebsite)}
|
hasPublishApp={!!(myApp || myWebsite)}
|
||||||
categories={categories}
|
categories={categories}
|
||||||
|
getQapps={getQapps}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{mode === "appInfo" && !selectedTab && <AppInfo app={selectedAppInfo} myName={myName} />}
|
{mode === "appInfo" && !selectedTab && <AppInfo app={selectedAppInfo} myName={myName} />}
|
||||||
|
@ -26,6 +26,7 @@ import IconClearInput from "../../assets/svgs/ClearInput.svg";
|
|||||||
import qappDevelopText from "../../assets/svgs/qappDevelopText.svg";
|
import qappDevelopText from "../../assets/svgs/qappDevelopText.svg";
|
||||||
import qappDots from "../../assets/svgs/qappDots.svg";
|
import qappDots from "../../assets/svgs/qappDots.svg";
|
||||||
import ReturnSVG from '../../assets/svgs/Return.svg'
|
import ReturnSVG from '../../assets/svgs/Return.svg'
|
||||||
|
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||||
|
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from "../../common/Spacer";
|
||||||
import { AppInfoSnippet } from "./AppInfoSnippet";
|
import { AppInfoSnippet } from "./AppInfoSnippet";
|
||||||
@ -41,6 +42,7 @@ const officialAppList = [
|
|||||||
"qombo",
|
"qombo",
|
||||||
"q-fund",
|
"q-fund",
|
||||||
"q-shop",
|
"q-shop",
|
||||||
|
"q-trade"
|
||||||
];
|
];
|
||||||
|
|
||||||
const ScrollerStyled = styled('div')({
|
const ScrollerStyled = styled('div')({
|
||||||
@ -76,7 +78,7 @@ const ScrollerStyled = styled('div')({
|
|||||||
"-ms-overflow-style": "none",
|
"-ms-overflow-style": "none",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, isShow, categories={categories} }) => {
|
export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, isShow, categories, getQapps }) => {
|
||||||
const [searchValue, setSearchValue] = useState("");
|
const [searchValue, setSearchValue] = useState("");
|
||||||
const virtuosoRef = useRef();
|
const virtuosoRef = useRef();
|
||||||
const { rootHeight } = useContext(MyContext);
|
const { rootHeight } = useContext(MyContext);
|
||||||
@ -132,6 +134,11 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, i
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: '20px',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}>
|
||||||
<AppsSearchContainer>
|
<AppsSearchContainer>
|
||||||
<AppsSearchLeft>
|
<AppsSearchLeft>
|
||||||
<img src={IconSearch} />
|
<img src={IconSearch} />
|
||||||
@ -159,6 +166,21 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, i
|
|||||||
)}
|
)}
|
||||||
</AppsSearchRight>
|
</AppsSearchRight>
|
||||||
</AppsSearchContainer>
|
</AppsSearchContainer>
|
||||||
|
<ButtonBase
|
||||||
|
onClick={(e) => {
|
||||||
|
getQapps()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RefreshIcon
|
||||||
|
|
||||||
|
sx={{
|
||||||
|
color: "rgba(250, 250, 250, 0.5)",
|
||||||
|
width: '40px',
|
||||||
|
height: 'auto'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ButtonBase>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</AppsWidthLimiter>
|
</AppsWidthLimiter>
|
||||||
<Spacer height="25px" />
|
<Spacer height="25px" />
|
||||||
|
@ -39,6 +39,8 @@ import { Spacer } from "../../common/Spacer";
|
|||||||
import { AppInfoSnippet } from "./AppInfoSnippet";
|
import { AppInfoSnippet } from "./AppInfoSnippet";
|
||||||
import { Virtuoso } from "react-virtuoso";
|
import { Virtuoso } from "react-virtuoso";
|
||||||
import { executeEvent } from "../../utils/events";
|
import { executeEvent } from "../../utils/events";
|
||||||
|
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AppsDesktopLibraryBody,
|
AppsDesktopLibraryBody,
|
||||||
AppsDesktopLibraryHeader,
|
AppsDesktopLibraryHeader,
|
||||||
@ -55,6 +57,7 @@ const officialAppList = [
|
|||||||
"qombo",
|
"qombo",
|
||||||
"q-fund",
|
"q-fund",
|
||||||
"q-shop",
|
"q-shop",
|
||||||
|
"q-trade"
|
||||||
];
|
];
|
||||||
|
|
||||||
const ScrollerStyled = styled("div")({
|
const ScrollerStyled = styled("div")({
|
||||||
@ -96,7 +99,7 @@ export const AppsLibraryDesktop = ({
|
|||||||
myName,
|
myName,
|
||||||
hasPublishApp,
|
hasPublishApp,
|
||||||
isShow,
|
isShow,
|
||||||
categories = { categories },
|
categories, getQapps
|
||||||
}) => {
|
}) => {
|
||||||
const [searchValue, setSearchValue] = useState("");
|
const [searchValue, setSearchValue] = useState("");
|
||||||
const virtuosoRef = useRef();
|
const virtuosoRef = useRef();
|
||||||
@ -169,6 +172,11 @@ export const AppsLibraryDesktop = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img src={qappLibraryText} />
|
<img src={qappLibraryText} />
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: '20px',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}>
|
||||||
<AppsSearchContainer
|
<AppsSearchContainer
|
||||||
sx={{
|
sx={{
|
||||||
width: "412px",
|
width: "412px",
|
||||||
@ -200,6 +208,21 @@ export const AppsLibraryDesktop = ({
|
|||||||
)}
|
)}
|
||||||
</AppsSearchRight>
|
</AppsSearchRight>
|
||||||
</AppsSearchContainer>
|
</AppsSearchContainer>
|
||||||
|
<ButtonBase
|
||||||
|
onClick={(e) => {
|
||||||
|
getQapps()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RefreshIcon
|
||||||
|
|
||||||
|
sx={{
|
||||||
|
color: "rgba(250, 250, 250, 0.5)",
|
||||||
|
width: '30px',
|
||||||
|
height: 'auto'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ButtonBase>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</AppsWidthLimiter>
|
</AppsWidthLimiter>
|
||||||
</AppsDesktopLibraryHeader>
|
</AppsDesktopLibraryHeader>
|
||||||
|
@ -139,7 +139,8 @@ const UIQortalRequests = [
|
|||||||
'GET_WALLET_BALANCE', 'GET_USER_WALLET_INFO', 'GET_CROSSCHAIN_SERVER_INFO',
|
'GET_WALLET_BALANCE', 'GET_USER_WALLET_INFO', 'GET_CROSSCHAIN_SERVER_INFO',
|
||||||
'GET_TX_ACTIVITY_SUMMARY', 'GET_FOREIGN_FEE', 'UPDATE_FOREIGN_FEE',
|
'GET_TX_ACTIVITY_SUMMARY', 'GET_FOREIGN_FEE', 'UPDATE_FOREIGN_FEE',
|
||||||
'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER',
|
'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER',
|
||||||
'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY'
|
'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER',
|
||||||
|
'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_GATEWAY'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
@ -369,6 +370,7 @@ isDOMContentLoaded: false
|
|||||||
|
|
||||||
const sendMessageToRuntime = (message, eventPort) => {
|
const sendMessageToRuntime = (message, eventPort) => {
|
||||||
chrome?.runtime?.sendMessage(message, (response) => {
|
chrome?.runtime?.sendMessage(message, (response) => {
|
||||||
|
console.log('runtimeres', response)
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
eventPort.postMessage({
|
eventPort.postMessage({
|
||||||
result: null,
|
result: null,
|
||||||
|
@ -93,7 +93,7 @@ export const MessageDisplay = ({ htmlContent, isReply }) => {
|
|||||||
if (res) {
|
if (res) {
|
||||||
const { service, name, identifier, path } = res;
|
const { service, name, identifier, path } = res;
|
||||||
executeEvent("addTab", { data: { service, name, identifier, path } });
|
executeEvent("addTab", { data: { service, name, identifier, path } });
|
||||||
executeEvent("open-dev-mode", { });
|
executeEvent("open-apps-mode", { });
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1313,10 +1313,10 @@ export const Group = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
subscribeToEvent("open-dev-mode", openDevModeFunc);
|
subscribeToEvent("open-apps-mode", openDevModeFunc);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribeFromEvent("open-dev-mode", openDevModeFunc);
|
unsubscribeFromEvent("open-apps-mode", openDevModeFunc);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import { WalletIcon } from "../../assets/Icons/WalletIcon";
|
|||||||
import { HubsIcon } from "../../assets/Icons/HubsIcon";
|
import { HubsIcon } from "../../assets/Icons/HubsIcon";
|
||||||
import { TradingIcon } from "../../assets/Icons/TradingIcon";
|
import { TradingIcon } from "../../assets/Icons/TradingIcon";
|
||||||
import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
|
import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
|
||||||
|
import { executeEvent } from "../../utils/events";
|
||||||
|
|
||||||
const IconWrapper = ({ children, label, color }) => {
|
const IconWrapper = ({ children, label, color }) => {
|
||||||
return (
|
return (
|
||||||
@ -184,7 +185,8 @@ export const MobileFooter = ({
|
|||||||
/>
|
/>
|
||||||
<BottomNavigationAction
|
<BottomNavigationAction
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
chrome.tabs.create({ url: "https://www.qort.trade"});
|
executeEvent("addTab", { data: { service: 'APP', name: 'q-trade' } });
|
||||||
|
executeEvent("open-apps-mode", { });
|
||||||
}}
|
}}
|
||||||
icon={
|
icon={
|
||||||
<IconWrapper label="Trading" color="rgba(250, 250, 250, 0.5)">
|
<IconWrapper label="Trading" color="rgba(250, 250, 250, 0.5)">
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { addForeignServer, addListItems, createPoll, decryptData, deleteListItems, deployAt, encryptData, getCrossChainServerInfo, getDaySummary, getForeignFee, getListItems, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getWalletBalance, joinGroup, publishMultipleQDNResources, publishQDNResource, removeForeignServer, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, updateForeignFee, voteOnPoll } from "./qortalRequests/get";
|
import { getApiKeyFromStorage } from "./background";
|
||||||
|
import { addForeignServer, addListItems, cancelSellOrder, createBuyOrder, createPoll, createSellOrder, decryptData, deleteListItems, deployAt, encryptData, getCrossChainServerInfo, getDaySummary, getForeignFee, getListItems, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getWalletBalance, joinGroup, publishMultipleQDNResources, publishQDNResource, removeForeignServer, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, updateForeignFee, voteOnPoll } from "./qortalRequests/get";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -26,6 +27,16 @@ function getLocalStorage(key) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isRunningGateway = async ()=> {
|
||||||
|
let isGateway = true;
|
||||||
|
const apiKey = await getApiKeyFromStorage();
|
||||||
|
if (apiKey && (apiKey?.url && !gateways.some(gateway => apiKey?.url?.includes(gateway)))) {
|
||||||
|
isGateway = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function setPermission(key, value) {
|
export async function setPermission(key, value) {
|
||||||
try {
|
try {
|
||||||
@ -422,6 +433,52 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "CREATE_TRADE_BUY_ORDER": {
|
||||||
|
const data = request.payload;
|
||||||
|
|
||||||
|
|
||||||
|
createBuyOrder(data, isFromExtension).then((res) => {
|
||||||
|
sendResponse(res);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
sendResponse({ error: error.message });
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "CREATE_TRADE_SELL_ORDER": {
|
||||||
|
const data = request.payload;
|
||||||
|
createSellOrder(data, isFromExtension).then((res) => {
|
||||||
|
sendResponse(res);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
sendResponse({ error: error.message });
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "CANCEL_TRADE_SELL_ORDER": {
|
||||||
|
const data = request.payload;
|
||||||
|
cancelSellOrder(data, isFromExtension).then((res) => {
|
||||||
|
sendResponse(res);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
sendResponse({ error: error.message });
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "IS_USING_GATEWAY": {
|
||||||
|
isRunningGateway().then((res) => {
|
||||||
|
console.log('isusing', res)
|
||||||
|
sendResponse({isGateway: res});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
sendResponse({ error: 'unable to determine if using gateway' });
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -12,7 +12,8 @@ import {
|
|||||||
joinGroup as joinGroupFunc,
|
joinGroup as joinGroupFunc,
|
||||||
sendQortFee,
|
sendQortFee,
|
||||||
sendCoin as sendCoinFunc,
|
sendCoin as sendCoinFunc,
|
||||||
isUsingLocal
|
isUsingLocal,
|
||||||
|
createBuyOrderTxQortalRequest
|
||||||
} from "../background";
|
} from "../background";
|
||||||
import { getNameInfo } from "../backgroundFunctions/encryption";
|
import { getNameInfo } from "../backgroundFunctions/encryption";
|
||||||
import { QORT_DECIMALS } from "../constants/constants";
|
import { QORT_DECIMALS } from "../constants/constants";
|
||||||
@ -26,10 +27,12 @@ import {
|
|||||||
uint8ArrayToBase64,
|
uint8ArrayToBase64,
|
||||||
} from "../qdn/encryption/group-encryption";
|
} from "../qdn/encryption/group-encryption";
|
||||||
import { publishData } from "../qdn/publish/pubish";
|
import { publishData } from "../qdn/publish/pubish";
|
||||||
import { getPermission, setPermission } from "../qortalRequests";
|
import { getPermission, setPermission, isRunningGateway } from "../qortalRequests";
|
||||||
import { createTransaction } from "../transactions/transactions";
|
import { createTransaction } from "../transactions/transactions";
|
||||||
import { mimeToExtensionMap } from "../utils/memeTypes";
|
import { mimeToExtensionMap } from "../utils/memeTypes";
|
||||||
|
import TradeBotCreateRequest from "../transactions/TradeBotCreateRequest";
|
||||||
|
import DeleteTradeOffer from "../transactions/TradeBotDeleteRequest";
|
||||||
|
import signTradeBotTransaction from "../transactions/signTradeBotTransaction";
|
||||||
|
|
||||||
const btcFeePerByte = 0.00000100
|
const btcFeePerByte = 0.00000100
|
||||||
const ltcFeePerByte = 0.00000030
|
const ltcFeePerByte = 0.00000030
|
||||||
@ -37,6 +40,19 @@ const dogeFeePerByte = 0.00001000
|
|||||||
const dgbFeePerByte = 0.00000010
|
const dgbFeePerByte = 0.00000010
|
||||||
const rvnFeePerByte = 0.00001125
|
const rvnFeePerByte = 0.00001125
|
||||||
|
|
||||||
|
const sellerForeignFee = {
|
||||||
|
'LITECOIN': {
|
||||||
|
value: '~0.00005',
|
||||||
|
ticker: 'LTC'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const _createPoll = async ({pollName, pollDescription, options}, isFromExtension) => {
|
const _createPoll = async ({pollName, pollDescription, options}, isFromExtension) => {
|
||||||
const fee = await getFee("CREATE_POLL");
|
const fee = await getFee("CREATE_POLL");
|
||||||
@ -1428,22 +1444,31 @@ export const getWalletBalance = async (data, bypassPermission?: boolean, isFromE
|
|||||||
const errorMsg = `Missing fields: ${missingFieldsString}`;
|
const errorMsg = `Missing fields: ${missingFieldsString}`;
|
||||||
throw new Error(errorMsg);
|
throw new Error(errorMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const value = (await getPermission(`qAPPAutoWalletBalance-${data.coin}`)) || false;
|
||||||
|
console.log('value', value)
|
||||||
|
let skip = false;
|
||||||
|
if (value) {
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
let resPermission
|
let resPermission
|
||||||
|
|
||||||
if(!bypassPermission){
|
if(!bypassPermission && !skip){
|
||||||
resPermission = await getUserPermission({
|
resPermission = await getUserPermission({
|
||||||
text1: "Do you give this application permission to fetch your",
|
text1: "Do you give this application permission to fetch your",
|
||||||
highlightedText: `${data.coin} balance`,
|
highlightedText: `${data.coin} balance`,
|
||||||
|
checkbox1: {
|
||||||
|
value: true,
|
||||||
|
label: "Always allow balance to be retrieved automatically",
|
||||||
|
},
|
||||||
}, isFromExtension);
|
}, isFromExtension);
|
||||||
} else {
|
}
|
||||||
resPermission = {
|
|
||||||
accepted: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { accepted } = resPermission;
|
const { accepted = false, checkbox1 = false } = resPermission || {};
|
||||||
|
if(resPermission){
|
||||||
if (accepted || bypassPermission) {
|
setPermission(`qAPPAutoWalletBalance-${data.coin}`, checkbox1);
|
||||||
|
}
|
||||||
|
if (accepted || bypassPermission || skip) {
|
||||||
let coin = data.coin;
|
let coin = data.coin;
|
||||||
const wallet = await getSaveWallet();
|
const wallet = await getSaveWallet();
|
||||||
const address = wallet.address0;
|
const address = wallet.address0;
|
||||||
@ -1530,7 +1555,7 @@ export const getWalletBalance = async (data, bypassPermission?: boolean, isFromE
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUserWalletFunc = async (coin) => {
|
export const getUserWalletFunc = async (coin) => {
|
||||||
let userWallet = {};
|
let userWallet = {};
|
||||||
const wallet = await getSaveWallet();
|
const wallet = await getSaveWallet();
|
||||||
const address = wallet.address0;
|
const address = wallet.address0;
|
||||||
@ -2461,3 +2486,310 @@ export const sendCoin = async (data, isFromExtension) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const createBuyOrder = async (data, isFromExtension) => {
|
||||||
|
console.log('data', data)
|
||||||
|
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 = `Missing fields: ${missingFieldsString}`;
|
||||||
|
throw new Error(errorMsg);
|
||||||
|
}
|
||||||
|
const isGateway = await isRunningGateway()
|
||||||
|
const foreignBlockchain = data.foreignBlockchain
|
||||||
|
const crosschainAtInfo = data.crosschainAtInfo;
|
||||||
|
const atAddresses = data.crosschainAtInfo?.map((order)=> order.qortalAtAddress);
|
||||||
|
console.log('test', isGateway, foreignBlockchain , crosschainAtInfo, atAddresses)
|
||||||
|
try {
|
||||||
|
const resPermission = await getUserPermission({
|
||||||
|
text1: "Do you give this application permission to perform a buy order?",
|
||||||
|
text2: `${atAddresses?.length}${" "}
|
||||||
|
${`buy order${
|
||||||
|
atAddresses?.length === 1 ? "" : "s"
|
||||||
|
}`}`,
|
||||||
|
text3: `${crosschainAtInfo?.reduce((latest, cur) => {
|
||||||
|
return latest + +cur?.qortAmount;
|
||||||
|
}, 0)} QORT FOR ${roundUpToDecimals(
|
||||||
|
crosschainAtInfo?.reduce((latest, cur) => {
|
||||||
|
return latest + +cur?.foreignAmount;
|
||||||
|
}, 0)
|
||||||
|
)}
|
||||||
|
${` ${crosschainAtInfo?.[0]?.foreignBlockchain}`}`,
|
||||||
|
highlightedText: `Is using gateway: ${isGateway}`,
|
||||||
|
fee: '',
|
||||||
|
foreignFee: `${sellerForeignFee[foreignBlockchain].value} ${sellerForeignFee[foreignBlockchain].ticker}`
|
||||||
|
}, isFromExtension);
|
||||||
|
const { accepted } = resPermission;
|
||||||
|
if (accepted) {
|
||||||
|
const resBuyOrder = await createBuyOrderTxQortalRequest(
|
||||||
|
{
|
||||||
|
crosschainAtInfo,
|
||||||
|
isGateway,
|
||||||
|
foreignBlockchain
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return resBuyOrder;
|
||||||
|
} else {
|
||||||
|
throw new Error("User declined request");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error?.message || "Failed to submit trade order.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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('Unable to update tradebot')
|
||||||
|
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: "Failed to Cancel Sell Order. Try again!",
|
||||||
|
failedTradeBot: {
|
||||||
|
atAddress: body.atAddress,
|
||||||
|
creatorAddress: body.creatorAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(res?.error){
|
||||||
|
return {
|
||||||
|
error: "Failed to Cancel Sell Order. Try again!",
|
||||||
|
failedTradeBot: {
|
||||||
|
atAddress: body.atAddress,
|
||||||
|
creatorAddress: body.creatorAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (res?.signature){
|
||||||
|
return res
|
||||||
|
} else {
|
||||||
|
throw new Error("Failed to Cancel Sell Order. Try again!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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('Unable to create tradebot')
|
||||||
|
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: "Failed to Create Sell Order. Try again!",
|
||||||
|
failedTradeBot: findFailedTradeBot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res?.signature){
|
||||||
|
return res
|
||||||
|
} else {
|
||||||
|
throw new Error("Failed to Create Sell Order. Try again!")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = `Missing fields: ${missingFieldsString}`;
|
||||||
|
throw new Error(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
const receivingAddress = await getUserWalletFunc('LTC')
|
||||||
|
try {
|
||||||
|
const resPermission = await getUserPermission({
|
||||||
|
text1: "Do you give this application permission to perform a sell order?",
|
||||||
|
text2: `${data.qortAmount}${" "}
|
||||||
|
${`QORT`}`,
|
||||||
|
text3: `FOR ${data.foreignAmount} ${data.foreignBlockchain}`,
|
||||||
|
fee: '0.02'
|
||||||
|
}, isFromExtension);
|
||||||
|
const { accepted } = resPermission;
|
||||||
|
if (accepted) {
|
||||||
|
const resKeyPair = await getKeyPair()
|
||||||
|
const parsedData = JSON.parse(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.001,
|
||||||
|
foreignBlockchain: data.foreignBlockchain,
|
||||||
|
foreignAmount: parseFloat(data.foreignAmount),
|
||||||
|
tradeTimeout: 120,
|
||||||
|
receivingAddress: receivingAddress.address
|
||||||
|
}, keyPair)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error("User declined request");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error?.message || "Failed to submit sell order.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const cancelSellOrder = async (data, isFromExtension) => {
|
||||||
|
|
||||||
|
const requiredFields = [
|
||||||
|
"qortAmount",
|
||||||
|
"foreignBlockchain",
|
||||||
|
"foreignAmount",
|
||||||
|
"atAddress"
|
||||||
|
];
|
||||||
|
const missingFields: string[] = [];
|
||||||
|
requiredFields.forEach((field) => {
|
||||||
|
if (!data[field]) {
|
||||||
|
missingFields.push(field);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (missingFields.length > 0) {
|
||||||
|
const missingFieldsString = missingFields.join(", ");
|
||||||
|
const errorMsg = `Missing fields: ${missingFieldsString}`;
|
||||||
|
throw new Error(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fee = await getFee("MESSAGE");
|
||||||
|
|
||||||
|
const resPermission = await getUserPermission({
|
||||||
|
text1: "Do you give this application permission to perform cancel a sell order?",
|
||||||
|
text2: `${data.qortAmount}${" "}
|
||||||
|
${`QORT`}`,
|
||||||
|
text3: `FOR ${data.foreignAmount} ${data.foreignBlockchain}`,
|
||||||
|
fee: fee.fee
|
||||||
|
}, isFromExtension);
|
||||||
|
const { accepted } = resPermission;
|
||||||
|
if (accepted) {
|
||||||
|
const resKeyPair = await getKeyPair()
|
||||||
|
const parsedData = JSON.parse(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("User declined request");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error?.message || "Failed to submit sell order.");
|
||||||
|
}
|
||||||
|
};
|
65
src/transactions/TradeBotCreateRequest.ts
Normal file
65
src/transactions/TradeBotCreateRequest.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CrossChain - TradeBot Create Request (Sell Action)
|
||||||
|
*
|
||||||
|
* These are special types of transactions (JSON ENCODED)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class TradeBotCreateRequest {
|
||||||
|
constructor() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
createTransaction(txnReq) {
|
||||||
|
this.creatorPublicKey(txnReq.creatorPublicKey)
|
||||||
|
this.qortAmount(txnReq.qortAmount)
|
||||||
|
this.fundingQortAmount(txnReq.fundingQortAmount)
|
||||||
|
this.foreignBlockchain(txnReq.foreignBlockchain)
|
||||||
|
this.foreignAmount(txnReq.foreignAmount)
|
||||||
|
this.tradeTimeout(txnReq.tradeTimeout)
|
||||||
|
this.receivingAddress(txnReq.receivingAddress)
|
||||||
|
|
||||||
|
return this.txnRequest()
|
||||||
|
}
|
||||||
|
|
||||||
|
creatorPublicKey(creatorPublicKey) {
|
||||||
|
this._creatorPublicKey = creatorPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
qortAmount(qortAmount) {
|
||||||
|
this._qortAmount = qortAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
fundingQortAmount(fundingQortAmount) {
|
||||||
|
this._fundingQortAmount = fundingQortAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
foreignBlockchain(foreignBlockchain) {
|
||||||
|
this._foreignBlockchain = foreignBlockchain
|
||||||
|
}
|
||||||
|
|
||||||
|
foreignAmount(foreignAmount) {
|
||||||
|
this._foreignAmount = foreignAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
tradeTimeout(tradeTimeout) {
|
||||||
|
this._tradeTimeout = tradeTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
receivingAddress(receivingAddress) {
|
||||||
|
this._receivingAddress = receivingAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
txnRequest() {
|
||||||
|
return {
|
||||||
|
creatorPublicKey: this._creatorPublicKey,
|
||||||
|
qortAmount: this._qortAmount,
|
||||||
|
fundingQortAmount: this._fundingQortAmount,
|
||||||
|
foreignBlockchain: this._foreignBlockchain,
|
||||||
|
foreignAmount: this._foreignAmount,
|
||||||
|
tradeTimeout: this._tradeTimeout,
|
||||||
|
receivingAddress: this._receivingAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
src/transactions/TradeBotDeleteRequest.ts
Normal file
35
src/transactions/TradeBotDeleteRequest.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CrossChain - DELETE TradeOffer
|
||||||
|
*
|
||||||
|
* These are special types of transactions (JSON ENCODED)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class DeleteTradeOffer {
|
||||||
|
constructor() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
createTransaction(txnReq) {
|
||||||
|
this.creatorPublicKey(txnReq.creatorPublicKey)
|
||||||
|
this.atAddress(txnReq.atAddress)
|
||||||
|
|
||||||
|
return this.txnRequest()
|
||||||
|
}
|
||||||
|
|
||||||
|
creatorPublicKey(creatorPublicKey) {
|
||||||
|
this._creatorPublicKey = creatorPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
atAddress(atAddress) {
|
||||||
|
this._atAddress = atAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
txnRequest() {
|
||||||
|
return {
|
||||||
|
creatorPublicKey: this._creatorPublicKey,
|
||||||
|
atAddress: this._atAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/transactions/signTradeBotTransaction.ts
Normal file
30
src/transactions/signTradeBotTransaction.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
|
||||||
|
import nacl from '../deps/nacl-fast'
|
||||||
|
import Base58 from '../deps/Base58'
|
||||||
|
import utils from '../utils/utils'
|
||||||
|
|
||||||
|
const signTradeBotTransaction = async (unsignedTxn, keyPair) => {
|
||||||
|
if (!unsignedTxn) {
|
||||||
|
throw new Error('Unsigned Transaction Bytes not defined')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keyPair) {
|
||||||
|
throw new Error('keyPair not defined')
|
||||||
|
}
|
||||||
|
|
||||||
|
const txnBuffer = Base58.decode(unsignedTxn)
|
||||||
|
|
||||||
|
if (keyPair.privateKey.length === undefined) {
|
||||||
|
const _privateKey = Object.keys(keyPair.privateKey).map(function (key) { return keyPair.privateKey[key] })
|
||||||
|
const privateKey = new Uint8Array(_privateKey)
|
||||||
|
const signature = nacl.sign.detached(txnBuffer, privateKey)
|
||||||
|
return utils.appendBuffer(txnBuffer, signature)
|
||||||
|
} else {
|
||||||
|
const signature = nacl.sign.detached(txnBuffer, keyPair.privateKey)
|
||||||
|
return utils.appendBuffer(txnBuffer, signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default signTradeBotTransaction
|
Loading…
x
Reference in New Issue
Block a user