From 40e8e316a8896529679b94fab61b352b67b6e521 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 6 Nov 2024 00:44:04 +0200 Subject: [PATCH] started buy order qortalrequset --- electron/src/setup.ts | 4 +- package-lock.json | 35 ++++++++ package.json | 1 + src/background.ts | 89 ++++++++++++++----- src/components/Apps/AppsCategoryDesktop.tsx | 4 +- src/components/Apps/AppsDesktop.tsx | 1 + src/components/Apps/AppsDevMode.tsx | 2 +- .../Apps/AppsDevModeTabComponent.tsx | 3 +- src/components/Apps/AppsLibraryDesktop.tsx | 2 +- .../Apps/useQortalMessageListener.tsx | 2 +- src/qortalRequests.ts | 21 ++++- src/qortalRequests/get.ts | 86 ++++++++++++++++-- 12 files changed, 213 insertions(+), 37 deletions(-) diff --git a/electron/src/setup.ts b/electron/src/setup.ts index b757b96..acb74cf 100644 --- a/electron/src/setup.ts +++ b/electron/src/setup.ts @@ -20,6 +20,7 @@ const defaultDomains = [ 'https://ext-node.qortal.link', 'wss://ext-node.qortal.link', 'https://appnode.qortal.org', + 'wss://appnode.qortal.org', "https://api.qortal.org", "https://api2.qortal.org", "https://appnode.qortal.org", @@ -27,7 +28,8 @@ const defaultDomains = [ "https://apinode1.qortalnodes.live", "https://apinode2.qortalnodes.live", "https://apinode3.qortalnodes.live", - "https://apinode4.qortalnodes.live" + "https://apinode4.qortalnodes.live", + "https://www.qort.trade" ]; // let allowedDomains: string[] = [...defaultDomains] diff --git a/package-lock.json b/package-lock.json index aaa7643..b950a34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "@transistorsoft/capacitor-background-fetch": "^6.0.1", "@types/chrome": "^0.0.263", "asmcrypto.js": "2.3.2", + "axios": "^1.7.7", "bcryptjs": "2.4.3", "buffer": "6.0.3", "chokidar": "^3.6.0", @@ -5099,6 +5100,16 @@ "node": ">=0.8" } }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -8146,6 +8157,25 @@ "node": ">= 10.0.0" } }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -13822,6 +13852,11 @@ "prosemirror-transform": "^1.1.0" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/package.json b/package.json index cd34e22..0abcf79 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@transistorsoft/capacitor-background-fetch": "^6.0.1", "@types/chrome": "^0.0.263", "asmcrypto.js": "2.3.2", + "axios": "^1.7.7", "bcryptjs": "2.4.3", "buffer": "6.0.3", "chokidar": "^3.6.0", diff --git a/src/background.ts b/src/background.ts index 7c609a7..c0af2aa 100644 --- a/src/background.ts +++ b/src/background.ts @@ -11,6 +11,7 @@ import { } from "./backgroundFunctions/encryption"; import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from "./constants/codes"; import Base58 from "./deps/Base58"; +import axios from 'axios' import { base64ToUint8Array, decryptSingle, @@ -1360,12 +1361,13 @@ export async function handleActiveGroupDataFromSocket({ groups, directs }) { } catch (error) {} } -async function sendChatForBuyOrder({ qortAddress, recipientPublicKey, message }) { +async function sendChatForBuyOrder({ qortAddress, recipientPublicKey, message, atAddresses }) { let _reference = new Uint8Array(64); self.crypto.getRandomValues(_reference); let sendTimestamp = Date.now(); - + const wallet = await getSaveWallet(); + const address = wallet.address0; let reference = Base58.encode(_reference); const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; @@ -1400,8 +1402,31 @@ async function sendChatForBuyOrder({ qortAddress, recipientPublicKey, message }) isEncrypted: 1, isText: 1, }); - if (!hasEnoughBalance) { - throw new Error('You must have at least 4 QORT to trade using the gateway.') + //TODO + // if (!hasEnoughBalance) { + if(!hasEnoughBalance){ + const _encryptedMessage = tx._encryptedMessage; + const encryptedMessageToBase58 = Base58.encode(_encryptedMessage); + const signature = "id-" + Date.now() + "-" + Math.floor(Math.random() * 1000) + const res = await axios.post( + `https://www.qort.trade/api/transaction/updatetxgateway`, + { + qortalAtAddresses: atAddresses, qortAddress: address, node: buyTradeNodeBaseUrl, status: "message-sent", encryptedMessageToBase58, signature , + reference, senderPublicKey: parsedData.publicKey, + sender: address, + }, + { + headers: { + "Content-Type": "application/json" + }, + } + ); + + return { + encryptedMessageToBase58, + status: "message-sent", + signature + } } const path = `${import.meta.env.BASE_URL}memory-pow.wasm.full`; @@ -1712,16 +1737,49 @@ export async function createBuyOrderTx({ crosschainAtInfo, useLocal }) { qortAddress: proxyAccountAddress, recipientPublicKey: proxyAccountPublicKey, message, + atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), }); + + if (res?.signature) { let responseMessage; - const message = await listenForChatMessageForBuyOrder({ - nodeBaseUrl: buyTradeNodeBaseUrl, - senderAddress: proxyAccountAddress, - senderPublicKey: proxyAccountPublicKey, - signature: res?.signature, - }); + if(res?.encryptedMessageToBase58){ + const message = await listenForChatMessageForBuyOrder({ + nodeBaseUrl: buyTradeNodeBaseUrl, + senderAddress: proxyAccountAddress, + senderPublicKey: proxyAccountPublicKey, + signature: res?.signature, + }); + responseMessage = { + callResponse: message.callResponse, + extra: { + message: message?.extra?.message, + senderAddress: address, + node: buyTradeNodeBaseUrl, + atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), + } + } + } else { + const message = await listenForChatMessageForBuyOrder({ + nodeBaseUrl: buyTradeNodeBaseUrl, + senderAddress: proxyAccountAddress, + senderPublicKey: proxyAccountPublicKey, + signature: res?.signature, + }); + + responseMessage = { + callResponse: message.callResponse, + extra: { + message: message?.extra?.message, + senderAddress: address, + node: buyTradeNodeBaseUrl, + atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), + } + } + } + + // const status = response.callResponse === true ? 'trade-ongoing' : 'trade-failed' // if (res?.encryptedMessageToBase58) { // return { @@ -1742,16 +1800,7 @@ export async function createBuyOrderTx({ crosschainAtInfo, useLocal }) { // node: buyTradeNodeBaseUrl, // qortAddress: address, // }; - responseMessage = { - callResponse: message.callResponse, - extra: { - message: message?.extra?.message, - senderAddress: address, - node: buyTradeNodeBaseUrl, - atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), - }, - encryptedMessageToBase58 - } + return responseMessage } else { diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index 91e818d..206eef0 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -201,8 +201,8 @@ export const AppsCategoryDesktop = ({ { const data = e.detail?.data; + if(e.detail?.isDevMode) return setSelectedTab(data); setTimeout(() => { diff --git a/src/components/Apps/AppsDevMode.tsx b/src/components/Apps/AppsDevMode.tsx index 6e997d0..13b24aa 100644 --- a/src/components/Apps/AppsDevMode.tsx +++ b/src/components/Apps/AppsDevMode.tsx @@ -113,7 +113,7 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop }, [tabs]); const setSelectedTabFunc = (e) => { const data = e.detail?.data; - + if(!e.detail?.isDevMode) return setSelectedTab(data); setTimeout(() => { executeEvent("appsDevModeSetTabsToNav", { diff --git a/src/components/Apps/AppsDevModeTabComponent.tsx b/src/components/Apps/AppsDevModeTabComponent.tsx index 174ef73..a0bc61a 100644 --- a/src/components/Apps/AppsDevModeTabComponent.tsx +++ b/src/components/Apps/AppsDevModeTabComponent.tsx @@ -16,7 +16,8 @@ export const AppsDevModeTabComponent = ({isSelected, app}) => { return } executeEvent('setSelectedTab', { - data: app + data: app, + isDevMode: true }) }}> { const [searchValue, setSearchValue] = useState(""); const virtuosoRef = useRef(); diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index 54ff99d..177d50d 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -183,7 +183,7 @@ const UIQortalRequests = [ 'GET_WALLET_BALANCE', 'GET_USER_WALLET_INFO', 'GET_CROSSCHAIN_SERVER_INFO', 'GET_TX_ACTIVITY_SUMMARY', 'GET_FOREIGN_FEE', 'UPDATE_FOREIGN_FEE', 'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER', - 'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER' + 'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER', 'CREATE_TRADE_SELL_ORDER' ]; diff --git a/src/qortalRequests.ts b/src/qortalRequests.ts index 0290da3..be6f2c8 100644 --- a/src/qortalRequests.ts +++ b/src/qortalRequests.ts @@ -1,4 +1,4 @@ -import { addForeignServer, addListItems, createBuyOrder, 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 { addForeignServer, addListItems, 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"; import { getData, storeData } from "./utils/chromeStorage"; @@ -604,6 +604,25 @@ function setLocalStorage(key, data) { } break; } + case "CREATE_TRADE_SELL_ORDER": { + try { + const res = await createSellOrder(request.payload, isFromExtension); + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + payload: res, + type: "backgroundMessageResponse", + }, event.origin); + } catch (error) { + event.source.postMessage({ + requestId: request.requestId, + action: request.action, + error: error.message, + type: "backgroundMessageResponse", + }, event.origin); + } + break; + } default: break; diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index d8ccd4a..3936f5c 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -1365,22 +1365,31 @@ export const getWalletBalance = async (data, bypassPermission?: boolean, isFromE const errorMsg = `Missing fields: ${missingFieldsString}`; 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 - if(!bypassPermission){ + if(!bypassPermission && !skip){ resPermission = await getUserPermission({ text1: "Do you give this application permission to fetch your", highlightedText: `${data.coin} balance`, + checkbox1: { + value: true, + label: "Always allow balance to be retrieved automatically", + }, }, isFromExtension); - } else { - resPermission = { - accepted: false - } + } + console.log('resPermission', resPermission) + const { accepted = false, checkbox1 = false } = resPermission || {}; + if(resPermission){ + setPermission(`qAPPAutoWalletBalance-${data.coin}`, checkbox1); } - - const { accepted } = resPermission; - - if (accepted || bypassPermission) { + if (accepted || bypassPermission || skip) { let coin = data.coin; const wallet = await getSaveWallet(); const address = wallet.address0; @@ -2424,6 +2433,65 @@ export const createBuyOrder = async (data, isFromExtension) => { throw new Error('Process Type must be either local or gateway') } + 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: `Using ${processType}`, + fee: '' + }, isFromExtension); + const { accepted } = resPermission; + if (accepted) { + const resBuyOrder = await createBuyOrderTx( + { + crosschainAtInfo, + useLocal: processType === 'local' ? true : false + } + ); + return resBuyOrder; + } else { + throw new Error("User declined request"); + } + } catch (error) { + throw new Error(error?.message || "Failed to submit trade order."); + } +}; + +export const createSellOrder = async (data, isFromExtension) => { + + const requiredFields = [ + "crosschainAtInfo", + "processType" + ]; + 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 crosschainAtInfo = data.crosschainAtInfo; + const atAddresses = data.crosschainAtInfo?.map((order)=> order.qortalAtAddress); + const processType = data.processType; + if(processType !== 'local' && processType !== 'gateway'){ + throw new Error('Process Type must be either local or gateway') + } + try { const resPermission = await getUserPermission({ text1: "Do you give this application permission to perform a buy order?",