added qortalrequests for trade

This commit is contained in:
PhilReact 2024-11-12 04:02:36 +02:00
parent 2638b24def
commit 7892a9a26d
15 changed files with 594 additions and 91 deletions

View File

@ -704,6 +704,9 @@ function App() {
const qortalRequestPermissonFromExtension = async (message, event) => { const qortalRequestPermissonFromExtension = async (message, event) => {
if (message.action === "QORTAL_REQUEST_PERMISSION") { if (message.action === "QORTAL_REQUEST_PERMISSION") {
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) {
event.source.postMessage( event.source.postMessage(
@ -1479,10 +1482,12 @@ function App() {
textDecoration: "underline", textDecoration: "underline",
}} }}
onClick={async () => { onClick={async () => {
await Browser.open({ url: "https://www.qort.trade" }); executeEvent("addTab", { data: { service: 'APP', name: 'q-trade' } });
executeEvent("open-apps-mode", { });
setIsOpenDrawerProfile(false);
}} }}
> >
Get QORT at qort.trade Get QORT at q-trade
</TextP> </TextP>
</AuthenticatedContainerInnerLeft> </AuthenticatedContainerInnerLeft>
<AuthenticatedContainerInnerRight> <AuthenticatedContainerInnerRight>
@ -2832,6 +2837,25 @@ await showInfo({
<Spacer height="15px" /> <Spacer height="15px" />
</> </>
)} )}
{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={{

View File

@ -24,6 +24,10 @@ export const sortablePinnedAppsAtom = atom({
},{ },{
name: 'Qombo', name: 'Qombo',
service: 'APP' service: 'APP'
},
{
name: 'Q-Trade',
service: 'APP'
} }
], ],
}); });

View File

@ -117,6 +117,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";
@ -174,6 +177,19 @@ export const clearAllQueues = () => {
}); });
}; };
export const getForeignKey = async (foreignBlockchain)=> {
const resKeyPair = await getKeyPair();
const parsedData = resKeyPair;
switch (foreignBlockchain) {
case "LITECOIN":
return parsedData.ltcPrivateKey
default:
return null
}
}
export const pauseAllQueues = () => controlAllQueues("pause"); export const pauseAllQueues = () => controlAllQueues("pause");
export const resumeAllQueues = () => controlAllQueues("resume"); export const resumeAllQueues = () => controlAllQueues("resume");
const checkDifference = (createdTimestamp) => { const checkDifference = (createdTimestamp) => {
@ -1581,27 +1597,29 @@ export async function decryptDirectFunc({ messages, involvingAddress }) {
return holdMessages; return holdMessages;
} }
export async function createBuyOrderTx({ crosschainAtInfo, useLocal }) { export async function createBuyOrderTx({ crosschainAtInfo, isGateway, foreignBlockchain }) {
try { try {
if (useLocal) {
if (!isGateway) {
const wallet = await getSaveWallet(); const wallet = await getSaveWallet();
const address = wallet.address0; const address = wallet.address0;
const resKeyPair = await getKeyPair();
const parsedData = resKeyPair;
const message = { const message = {
addresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), addresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
foreignKey: parsedData.ltcPrivateKey, foreignKey: await getForeignKey(foreignBlockchain),
receivingAddress: address, receivingAddress: address,
}; };
let responseVar; let responseVar;
const txn = new TradeBotRespondMultipleRequest().createTransaction( const txn = new TradeBotRespondMultipleRequest().createTransaction(
message message
); );
const apiKey = await getApiKeyFromStorage();
const url = await createEndpoint('/crosschain/tradebot/respondmultiple')
const responseFetch = await fetch( const responseFetch = await fetch(
`http://127.0.0.1:12391/crosschain/tradebot/respondmultiple?apiKey=${apiKey?.apikey}`, url,
{ {
method: "POST", method: "POST",
headers: { headers: {
@ -1630,7 +1648,7 @@ export async function createBuyOrderTx({ crosschainAtInfo, useLocal }) {
message: "Transaction processed successfully!", message: "Transaction processed successfully!",
atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
senderAddress: address, senderAddress: address,
node: 'http://127.0.0.1:12391' node: url
}, },
}; };
} else { } else {
@ -1640,7 +1658,7 @@ export async function createBuyOrderTx({ crosschainAtInfo, useLocal }) {
message: response, message: response,
atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
senderAddress: address, senderAddress: address,
node: 'http://127.0.0.1:12391' node: url
}, },
}; };
} }
@ -1650,20 +1668,21 @@ export async function createBuyOrderTx({ crosschainAtInfo, useLocal }) {
const wallet = await getSaveWallet(); const wallet = await getSaveWallet();
const address = wallet.address0; const address = wallet.address0;
const resKeyPair = await getKeyPair();
const parsedData = resKeyPair;
const message = { const message = {
addresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), addresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
foreignKey: parsedData.ltcPrivateKey, foreignKey: await getForeignKey(foreignBlockchain),
receivingAddress: address, receivingAddress: address,
}; };
const res = await sendChatForBuyOrder({ const res = await sendChatForBuyOrder({
qortAddress: proxyAccountAddress, qortAddress: proxyAccountAddress,
recipientPublicKey: proxyAccountPublicKey, recipientPublicKey: proxyAccountPublicKey,
message, message,
atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
}); });
if (res?.signature) { if (res?.signature) {
let responseMessage;
const message = await listenForChatMessageForBuyOrder({ const message = await listenForChatMessageForBuyOrder({
nodeBaseUrl: buyTradeNodeBaseUrl, nodeBaseUrl: buyTradeNodeBaseUrl,
@ -1671,35 +1690,15 @@ export async function createBuyOrderTx({ crosschainAtInfo, useLocal }) {
senderPublicKey: proxyAccountPublicKey, senderPublicKey: proxyAccountPublicKey,
signature: res?.signature, signature: res?.signature,
}); });
// const status = response.callResponse === true ? 'trade-ongoing' : 'trade-failed'
// if (res?.encryptedMessageToBase58) { const responseMessage = {
// return {
// atAddresses: crosschainAtInfo.map((order) => order.qortalAtAddress),
// encryptedMessageToBase58: res?.encryptedMessageToBase58,
// node: buyTradeNodeBaseUrl,
// qortAddress: address,
// chatSignature: res?.signature,
// senderPublicKey: parsedData.publicKey,
// sender: address,
// reference: res?.reference,
// response.callResponse
// };
// }
// return {
// atAddresses: crosschainAtInfo.map((order) => order.qortalAtAddress),
// chatSignature: res?.signature,
// node: buyTradeNodeBaseUrl,
// qortAddress: address,
// };
responseMessage = {
callResponse: message.callResponse, callResponse: message.callResponse,
extra: { extra: {
message: message?.extra?.message, message: message?.extra?.message,
senderAddress: address, senderAddress: address,
node: buyTradeNodeBaseUrl, node: buyTradeNodeBaseUrl,
atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress),
}, }
encryptedMessageToBase58
} }
return responseMessage return responseMessage
@ -2306,14 +2305,7 @@ async function listenForChatMessageForBuyOrder({
senderPublicKey senderPublicKey
); );
// chrome.tabs.query({}, function (tabs) { return parsedMessageObj
// tabs.forEach((tab) => {
// chrome.tabs.sendMessage(tab.id, {
// type: "RESPONSE_FOR_TRADES",
// message: parsedMessageObj,
// });
// });
// });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
throw new Error(error.message); throw new Error(error.message);

View File

@ -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} />}

View File

@ -39,6 +39,7 @@ const officialAppList = [
"qombo", "qombo",
"q-fund", "q-fund",
"q-shop", "q-shop",
"q-trade"
]; ];
const ScrollerStyled = styled('div')({ const ScrollerStyled = styled('div')({

View File

@ -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: '30px',
height: 'auto'
}}
/>
</ButtonBase>
</Box>
</Box> </Box>
</AppsWidthLimiter> </AppsWidthLimiter>
<Spacer height="25px" /> <Spacer height="25px" />

View File

@ -183,7 +183,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', 'CREATE_TRADE_BUY_ORDER' 'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER',
'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_GATEWAY'
]; ];
@ -434,7 +435,7 @@ isDOMContentLoaded: false
if (event?.data?.requestedHandler !== 'UI') return; if (event?.data?.requestedHandler !== 'UI') return;
const sendMessageToRuntime = (message, eventPort) => { const sendMessageToRuntime = (message, eventPort) => {
window.sendMessage(message.action, message.payload, 60000, message.isExtension) window.sendMessage(message.action, message.payload, 300000, message.isExtension)
.then((response) => { .then((response) => {
if (response.error) { if (response.error) {
eventPort.postMessage({ eventPort.postMessage({

View File

@ -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", { });
} }
} }

View File

@ -1314,10 +1314,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);
}; };
}, []); }, []);

View File

@ -17,6 +17,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 (
@ -209,7 +210,8 @@ export const MobileFooter = ({
/> />
<BottomNavigationAction <BottomNavigationAction
onClick={async () => { onClick={async () => {
await Browser.open({ url: 'https://www.qort.trade' }); executeEvent("addTab", { data: { service: 'APP', name: 'q-trade' } });
executeEvent("open-apps-mode", { });
}} }}
icon={ icon={

View File

@ -1,4 +1,5 @@
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 { gateways, getApiKeyFromStorage } from "./background";
import { addForeignServer, addListItems, cancelSellOrder, 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 { getData, storeData } from "./utils/chromeStorage"; import { getData, storeData } from "./utils/chromeStorage";
@ -18,6 +19,16 @@ function setLocalStorage(key, data) {
}); });
} }
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 {
@ -605,6 +616,48 @@ function setLocalStorage(key, data) {
break; break;
} }
case "CANCEL_TRADE_SELL_ORDER": {
try {
const res = await cancelSellOrder(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;
}
case "IS_USING_GATEWAY": {
try {
console.log('isusing going')
let isGateway = await isRunningGateway()
console.log('isGateway', isGateway)
event.source.postMessage({
requestId: request.requestId,
action: request.action,
payload: {isGateway},
type: "backgroundMessageResponse",
}, event.origin);
} catch (error) {
console.log('isusing going', error)
event.source.postMessage({
requestId: request.requestId,
action: request.action,
error: error?.message,
type: "backgroundMessageResponse",
}, event.origin);
}
break;
}
default: default:
break; break;
} }

View File

@ -28,10 +28,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
@ -39,6 +41,13 @@ 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) { 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;
@ -1359,22 +1368,30 @@ 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 = false, checkbox1 = false } = resPermission || {};
if(resPermission){
setPermission(`qAPPAutoWalletBalance-${data.coin}`, checkbox1);
} }
if (accepted || bypassPermission || skip) {
const { accepted } = resPermission;
if (accepted || bypassPermission) {
let coin = data.coin; let coin = data.coin;
const wallet = await getSaveWallet(); const wallet = await getSaveWallet();
const address = wallet.address0; const address = wallet.address0;
@ -1461,7 +1478,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;
@ -2394,11 +2411,13 @@ export const sendCoin = async (data, isFromExtension) => {
}; };
export const createBuyOrder = async (data, isFromExtension) => { export const createBuyOrder = async (data, isFromExtension) => {
const requiredFields = [ const requiredFields = [
"crosschainAtInfo", "crosschainAtInfo",
"processType" "foreignBlockchain"
]; ];
const missingFields: string[] = []; const missingFields: string[] = [];
requiredFields.forEach((field) => { requiredFields.forEach((field) => {
@ -2411,12 +2430,10 @@ export const createBuyOrder = async (data, isFromExtension) => {
const errorMsg = `Missing fields: ${missingFieldsString}`; const errorMsg = `Missing fields: ${missingFieldsString}`;
throw new Error(errorMsg); throw new Error(errorMsg);
} }
const isGateway = await isRunningGateway()
const foreignBlockchain = data.foreignBlockchain
const crosschainAtInfo = data.crosschainAtInfo; const crosschainAtInfo = data.crosschainAtInfo;
const atAddresses = data.crosschainAtInfo?.map((order)=> order.qortalAtAddress); 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 { try {
const resPermission = await getUserPermission({ const resPermission = await getUserPermission({
@ -2433,15 +2450,17 @@ export const createBuyOrder = async (data, isFromExtension) => {
}, 0) }, 0)
)} )}
${` ${crosschainAtInfo?.[0]?.foreignBlockchain}`}`, ${` ${crosschainAtInfo?.[0]?.foreignBlockchain}`}`,
highlightedText: `Using ${processType}`, highlightedText: `Is using gateway: ${isGateway}`,
fee: '' fee: '',
foreignFee: `${sellerForeignFee[foreignBlockchain].value} ${sellerForeignFee[foreignBlockchain].ticker}`
}, isFromExtension); }, isFromExtension);
const { accepted } = resPermission; const { accepted } = resPermission;
if (accepted) { if (accepted) {
const resBuyOrder = await createBuyOrderTx( const resBuyOrder = await createBuyOrderTx(
{ {
crosschainAtInfo, crosschainAtInfo,
useLocal: processType === 'local' ? true : false isGateway,
foreignBlockchain
} }
); );
return resBuyOrder; return resBuyOrder;
@ -2452,3 +2471,248 @@ export const createBuyOrder = async (data, isFromExtension) => {
throw new Error(error?.message || "Failed to submit trade order."); 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 = 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 = 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.");
}
};

View 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
}
}
}

View 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
}
}
}

View 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