mirror of
https://github.com/Qortal/chrome-extension.git
synced 2025-02-11 17:55:49 +00:00
buyorder approval
This commit is contained in:
parent
6c2c3e02be
commit
62aed9fea2
@ -10,7 +10,6 @@
|
||||
// In your content script
|
||||
document.addEventListener('qortalExtensionRequests', async (event) => {
|
||||
const { type, payload, requestId, timeout } = event.detail; // Capture the requestId
|
||||
|
||||
if (type === 'REQUEST_USER_INFO') {
|
||||
const hostname = window.location.hostname
|
||||
const res = await connection(hostname)
|
||||
@ -92,7 +91,37 @@ document.addEventListener('qortalExtensionRequests', async (event) => {
|
||||
}));
|
||||
}
|
||||
});
|
||||
} else if (type === 'REQUEST_AUTHENTICATION') {
|
||||
} else if (type === 'REQUEST_BUY_ORDER') {
|
||||
const hostname = window.location.hostname
|
||||
const res = await connection(hostname)
|
||||
if(!res){
|
||||
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||
detail: { type: "BUY_ORDER", data: {
|
||||
error: "Not authorized"
|
||||
}, requestId }
|
||||
}));
|
||||
return
|
||||
}
|
||||
|
||||
chrome.runtime.sendMessage({ action: "buyOrder", payload: {
|
||||
qortalAtAddress: payload.qortalAtAddress,
|
||||
hostname
|
||||
|
||||
}, timeout}, (response) => {
|
||||
if (response.error) {
|
||||
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||
detail: { type: "BUY_ORDER", data: {
|
||||
error: response.error
|
||||
}, requestId }
|
||||
}));
|
||||
} else {
|
||||
// Include the requestId in the detail when dispatching the response
|
||||
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||
detail: { type: "BUY_ORDER", data: response, requestId }
|
||||
}));
|
||||
}
|
||||
});
|
||||
} else if (type === 'REQUEST_AUTHENTICATION') {
|
||||
const hostname = window.location.hostname
|
||||
const res = await connection(hostname)
|
||||
if(!res){
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Qortal",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"icons": {
|
||||
"16": "qort.png",
|
||||
"32": "qort.png",
|
||||
@ -16,10 +16,14 @@
|
||||
},
|
||||
"permissions": [ "storage", "system.display", "activeTab", "tabs"
|
||||
],
|
||||
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["content-script.js"]
|
||||
}
|
||||
]
|
||||
],
|
||||
"content_security_policy": {
|
||||
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; connect-src 'self' https://api.qortal.org https://api2.qortal.org https://appnode.qortal.org https://apinode.qortalnodes.live https://apinode1.qortalnodes.live https://apinode2.qortalnodes.live https://apinode3.qortalnodes.live https://apinode4.qortalnodes.live;"
|
||||
}
|
||||
}
|
||||
|
BIN
public/memory-pow.wasm.full
Normal file
BIN
public/memory-pow.wasm.full
Normal file
Binary file not shown.
143
src/App.tsx
143
src/App.tsx
@ -55,6 +55,7 @@ type extStates =
|
||||
| "transfer-success-regular"
|
||||
| "transfer-success-request"
|
||||
| "wallet-dropped"
|
||||
| "web-app-request-buy-order"
|
||||
;
|
||||
|
||||
function App() {
|
||||
@ -63,6 +64,8 @@ function App() {
|
||||
const [rawWallet, setRawWallet] = useState<any>(null);
|
||||
const [decryptedWallet, setdecryptedWallet] = useState<any>(null);
|
||||
const [requestConnection, setRequestConnection] = useState<any>(null);
|
||||
const [requestBuyOrder, setRequestBuyOrder] = useState<any>(null);
|
||||
|
||||
const [requestAuthentication, setRequestAuthentication] = useState<any>(null);
|
||||
const [userInfo, setUserInfo] = useState<any>(null);
|
||||
const [balance, setBalance] = useState<any>(null);
|
||||
@ -266,6 +269,10 @@ function App() {
|
||||
// Update the component state with the received 'sendqort' state
|
||||
setRequestConnection(message.payload);
|
||||
setExtstate("web-app-request-connection");
|
||||
} else if (message.action === "UPDATE_STATE_REQUEST_BUY_ORDER") {
|
||||
// Update the component state with the received 'sendqort' state
|
||||
setRequestBuyOrder(message.payload);
|
||||
setExtstate("web-app-request-buy-order");
|
||||
} else if (message.action === "UPDATE_STATE_REQUEST_AUTHENTICATION") {
|
||||
// Update the component state with the received 'sendqort' state
|
||||
setRequestAuthentication(message.payload);
|
||||
@ -301,10 +308,7 @@ function App() {
|
||||
);
|
||||
return;
|
||||
}
|
||||
// if (!paymentPassword) {
|
||||
// setSendPaymentError("Please enter your wallet password");
|
||||
// return;
|
||||
// }
|
||||
|
||||
setIsLoading(true)
|
||||
chrome.runtime.sendMessage(
|
||||
{
|
||||
@ -321,7 +325,49 @@ function App() {
|
||||
if (response === true) {
|
||||
setExtstate("transfer-success-request");
|
||||
setCountdown(null);
|
||||
// setSendPaymentSuccess("Payment successfully sent");
|
||||
} else {
|
||||
|
||||
setSendPaymentError(
|
||||
response?.error || "Unable to perform payment. Please try again."
|
||||
);
|
||||
}
|
||||
setIsLoading(false)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const confirmBuyOrder = (isDecline: boolean) => {
|
||||
if (isDecline) {
|
||||
chrome.runtime.sendMessage(
|
||||
{
|
||||
action: "buyOrderConfirmation",
|
||||
payload: {
|
||||
crosschainAtInfo: requestBuyOrder?.crosschainAtInfo,
|
||||
interactionId: requestBuyOrder?.interactionId,
|
||||
isDecline: true,
|
||||
},
|
||||
},
|
||||
(response) => {
|
||||
window.close();
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true)
|
||||
chrome.runtime.sendMessage(
|
||||
{
|
||||
action: "buyOrderConfirmation",
|
||||
payload: {
|
||||
crosschainAtInfo: requestBuyOrder?.crosschainAtInfo,
|
||||
interactionId: requestBuyOrder?.interactionId,
|
||||
isDecline: false,
|
||||
},
|
||||
},
|
||||
(response) => {
|
||||
if (response === true) {
|
||||
setExtstate("transfer-success-request");
|
||||
setCountdown(null);
|
||||
} else {
|
||||
|
||||
setSendPaymentError(
|
||||
@ -362,7 +408,7 @@ function App() {
|
||||
chrome.runtime.sendMessage({ action: "getWalletInfo" }, (response) => {
|
||||
if (response && response?.walletInfo) {
|
||||
setRawWallet(response?.walletInfo);
|
||||
if(holdRefExtState.current === 'web-app-request-payment' || holdRefExtState.current === 'web-app-request-connection') return
|
||||
if(holdRefExtState.current === 'web-app-request-payment' || holdRefExtState.current === 'web-app-request-connection' || holdRefExtState.current === 'web-app-request-buy-order') return
|
||||
setExtstate("authenticated");
|
||||
}
|
||||
});
|
||||
@ -510,6 +556,7 @@ function App() {
|
||||
setRawWallet(null);
|
||||
setdecryptedWallet(null);
|
||||
setRequestConnection(null);
|
||||
setRequestBuyOrder(null)
|
||||
setRequestAuthentication(null);
|
||||
setUserInfo(null);
|
||||
setBalance(null);
|
||||
@ -816,6 +863,90 @@ function App() {
|
||||
Send
|
||||
</CustomButton>
|
||||
</>
|
||||
)}
|
||||
{extState === "web-app-request-buy-order" && (
|
||||
<>
|
||||
<Spacer height="100px" />
|
||||
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
lineHeight: "15px",
|
||||
}}
|
||||
>
|
||||
The Application <br></br>{" "}
|
||||
<TextItalic>{requestBuyOrder?.hostname}</TextItalic> <br></br>
|
||||
<TextSpan>is requesting a buy order</TextSpan>
|
||||
</TextP>
|
||||
<Spacer height="10px" />
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
lineHeight: "15px",
|
||||
fontSize: "10px",
|
||||
}}
|
||||
>
|
||||
{requestBuyOrder?.crosschainAtInfo?.qortAmount} QORT
|
||||
</TextP>
|
||||
<Spacer height="15px" />
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
lineHeight: "15px",
|
||||
fontSize: "10px",
|
||||
}}
|
||||
>
|
||||
FOR
|
||||
</TextP>
|
||||
<Spacer height="15px" />
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
lineHeight: "24px",
|
||||
fontSize: "20px",
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
{requestBuyOrder?.crosschainAtInfo?.expectedForeignAmount} {requestBuyOrder?.crosschainAtInfo?.foreignBlockchain}
|
||||
</TextP>
|
||||
{/* <Spacer height="29px" />
|
||||
|
||||
<CustomLabel htmlFor="standard-adornment-password">
|
||||
Confirm Wallet Password
|
||||
</CustomLabel>
|
||||
<Spacer height="5px" />
|
||||
<PasswordField
|
||||
id="standard-adornment-password"
|
||||
value={paymentPassword}
|
||||
onChange={(e) => setPaymentPassword(e.target.value)}
|
||||
/> */}
|
||||
<Spacer height="29px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "14px",
|
||||
}}
|
||||
>
|
||||
<CustomButton
|
||||
sx={{
|
||||
minWidth: "102px",
|
||||
}}
|
||||
onClick={() => confirmBuyOrder(false)}
|
||||
>
|
||||
accept
|
||||
</CustomButton>
|
||||
<CustomButton
|
||||
sx={{
|
||||
minWidth: "102px",
|
||||
}}
|
||||
onClick={() => confirmBuyOrder(true)}
|
||||
>
|
||||
decline
|
||||
</CustomButton>
|
||||
</Box>
|
||||
<ErrorText>{sendPaymentError}</ErrorText>
|
||||
</>
|
||||
)}
|
||||
{extState === "web-app-request-payment" && (
|
||||
<>
|
||||
|
@ -1,19 +1,13 @@
|
||||
// @ts-nocheck
|
||||
import Base58 from "./deps/Base58";
|
||||
import { signChat } from "./transactions/signChat";
|
||||
import { createTransaction } from "./transactions/transactions";
|
||||
import { decryptChatMessage } from "./utils/decryptChatMessage";
|
||||
import { decryptStoredWallet } from "./utils/decryptWallet";
|
||||
import PhraseWallet from "./utils/generateWallet/phrase-wallet";
|
||||
import { validateAddress } from "./utils/validateAddress";
|
||||
import { Sha256 } from 'asmcrypto.js'
|
||||
|
||||
// chrome.storage.local.clear(function() {
|
||||
// var error = chrome.runtime.lastError;
|
||||
// if (error) {
|
||||
// console.error(error);
|
||||
// } else {
|
||||
// console.log('Local storage cleared');
|
||||
// }
|
||||
// });
|
||||
|
||||
export const walletVersion = 2;
|
||||
// List of your API endpoints
|
||||
@ -114,6 +108,15 @@ async function connection(hostname) {
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
async function getTradeInfo(qortalAtAddress) {
|
||||
const validApi = await findUsableApi();
|
||||
const response = await fetch(validApi + "/crosschain/trade/" + qortalAtAddress);
|
||||
|
||||
if (!response?.ok) throw new Error("Cannot crosschain trade information");
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
async function getBalanceInfo() {
|
||||
const wallet = await getSaveWallet();
|
||||
const address = wallet.address0;
|
||||
@ -125,6 +128,24 @@ async function getBalanceInfo() {
|
||||
return data;
|
||||
}
|
||||
|
||||
const processTransactionVersion2Chat = async (body: any, validApi: string) => {
|
||||
// const validApi = await findUsableApi();
|
||||
const url = validApi + "/transactions/process?apiVersion=2";
|
||||
return fetch(url, {
|
||||
method: "POST",
|
||||
headers: {},
|
||||
body: Base58.encode(body),
|
||||
}).then(async (response) => {
|
||||
try {
|
||||
const json = await response.clone().json();
|
||||
return json;
|
||||
} catch (e) {
|
||||
return await response.text();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const processTransactionVersion2 = async (body: any, validApi: string) => {
|
||||
// const validApi = await findUsableApi();
|
||||
const url = validApi + "/transactions/process?apiVersion=2";
|
||||
@ -237,9 +258,11 @@ async function decryptWallet({password, wallet, walletVersion}) {
|
||||
const response = await decryptStoredWallet(password, wallet);
|
||||
const wallet2 = new PhraseWallet(response, walletVersion);
|
||||
const keyPair = wallet2._addresses[0].keyPair;
|
||||
const ltcPrivateKey = wallet2._addresses[0].ltcWallet.derivedMasterPrivateKey
|
||||
const toSave = {
|
||||
privateKey: Base58.encode(keyPair.privateKey),
|
||||
publicKey: Base58.encode(keyPair.publicKey)
|
||||
publicKey: Base58.encode(keyPair.publicKey),
|
||||
ltcPrivateKey: ltcPrivateKey
|
||||
}
|
||||
const dataString = JSON.stringify(toSave)
|
||||
await new Promise((resolve, reject) => {
|
||||
@ -272,6 +295,152 @@ async function decryptWallet({password, wallet, walletVersion}) {
|
||||
}
|
||||
}
|
||||
|
||||
async function signChatFunc(chatBytesArray, chatNonce, validApi, keyPair ){
|
||||
let response
|
||||
try {
|
||||
const signedChatBytes = signChat(
|
||||
chatBytesArray,
|
||||
chatNonce,
|
||||
keyPair
|
||||
)
|
||||
const res = await processTransactionVersion2Chat(signedChatBytes, validApi)
|
||||
response = res
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.error(e.message)
|
||||
response = false
|
||||
}
|
||||
return response
|
||||
}
|
||||
function sbrk(size, heap) {
|
||||
let brk = 512 * 1024 // stack top
|
||||
let old = brk
|
||||
brk += size
|
||||
if (brk > heap.length) throw new Error('heap exhausted')
|
||||
return old
|
||||
}
|
||||
|
||||
const computePow = async ({chatBytes, path, difficulty}) => {
|
||||
let response = null
|
||||
await new Promise((resolve, reject) => {
|
||||
const _chatBytesArray = Object.keys(chatBytes).map(function (key) {
|
||||
return chatBytes[key]
|
||||
})
|
||||
const chatBytesArray = new Uint8Array(_chatBytesArray)
|
||||
const chatBytesHash = new Sha256().process(chatBytesArray).finish().result
|
||||
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
|
||||
const heap = new Uint8Array(memory.buffer)
|
||||
|
||||
const hashPtr = sbrk(32, heap)
|
||||
const hashAry = new Uint8Array(memory.buffer, hashPtr, 32)
|
||||
hashAry.set(chatBytesHash)
|
||||
const workBufferLength = 8 * 1024 * 1024
|
||||
const workBufferPtr = sbrk(workBufferLength, heap)
|
||||
const importObject = {
|
||||
env: {
|
||||
memory: memory
|
||||
}
|
||||
}
|
||||
function loadWebAssembly(filename, imports) {
|
||||
// Fetch the file and compile it
|
||||
return fetch(filename).then(response => response.arrayBuffer()).then(buffer => WebAssembly.compile(buffer)).then(module => {
|
||||
// Create the instance.
|
||||
return new WebAssembly.Instance(module, importObject)
|
||||
})
|
||||
}
|
||||
loadWebAssembly(path)
|
||||
.then(wasmModule => {
|
||||
response = {
|
||||
nonce: wasmModule.exports.compute2(hashPtr, workBufferPtr, workBufferLength, difficulty), chatBytesArray
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
return response
|
||||
}
|
||||
|
||||
async function sendChat({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 difficulty = 8;
|
||||
const callRequest = `curl -X 'POST' 'http://localhost:12391/crosschain/tradebot/respond' -H 'accept: text/plain' -H 'X-API-KEY: keykeykeykey' -H 'Content-Type: application/json' -d '{ "atAddress": "${message.atAddress}", "foreignKey": "${message.foreignKey}", "receivingAddress": "${message.receivingAddress}" }'`;
|
||||
|
||||
// Construct the final JSON object
|
||||
const finalJson = {
|
||||
callRequest: callRequest,
|
||||
extra: "whatever additional data goes here"
|
||||
};
|
||||
const messageStringified = JSON.stringify(finalJson)
|
||||
const {chatBytes} = await createTransaction(
|
||||
18,
|
||||
keyPair,
|
||||
{
|
||||
timestamp: sendTimestamp,
|
||||
recipient: qortAddress,
|
||||
recipientPublicKey: recipientPublicKey,
|
||||
hasChatReference: 0,
|
||||
message: messageStringified,
|
||||
lastReference: reference,
|
||||
proofOfWorkNonce: 0,
|
||||
isEncrypted: 1,
|
||||
isText: 1
|
||||
},
|
||||
|
||||
)
|
||||
const path = chrome.runtime.getURL('memory-pow.wasm.full');
|
||||
|
||||
|
||||
|
||||
|
||||
const {nonce, chatBytesArray} = await computePow({ chatBytes, path, difficulty })
|
||||
let _response = await signChatFunc(chatBytesArray,
|
||||
nonce, "https://appnode.qortal.org", keyPair
|
||||
)
|
||||
return _response
|
||||
}
|
||||
|
||||
async function createBuyOrderTx({crosschainAtInfo}){
|
||||
try {
|
||||
const wallet = await getSaveWallet();
|
||||
const address = wallet.address0;
|
||||
|
||||
const resKeyPair = await getKeyPair()
|
||||
const parsedData = JSON.parse(resKeyPair)
|
||||
const message = {
|
||||
atAddress: crosschainAtInfo.qortalAtAddress,
|
||||
foreignKey: parsedData.ltcPrivateKey,
|
||||
receivingAddress: address
|
||||
}
|
||||
const res = await sendChat({qortAddress: "QXPejUe5Za1KD3zCMViWCX35AreMQ9H7ku", recipientPublicKey: "5hP6stDWybojoDw5t8z9D51nV945oMPX7qBd29rhX1G7", message })
|
||||
if(res?.signature){
|
||||
const decryptedMessage = await listenForChatMessageForBuyOrder({
|
||||
nodeBaseUrl: "https://appnode.qortal.org",
|
||||
senderAddress: "QXPejUe5Za1KD3zCMViWCX35AreMQ9H7ku",
|
||||
senderPublicKey: "5hP6stDWybojoDw5t8z9D51nV945oMPX7qBd29rhX1G7",
|
||||
signature: res?.signature,
|
||||
|
||||
})
|
||||
return decryptedMessage
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendCoin({ password, amount, receiver }, skipConfirmPassword) {
|
||||
try {
|
||||
const confirmReceiver = await getNameOrAddress(receiver);
|
||||
@ -316,7 +485,7 @@ async function sendCoin({ password, amount, receiver }, skipConfirmPassword) {
|
||||
|
||||
function fetchMessages(apiCall) {
|
||||
let retryDelay = 2000; // Start with a 2-second delay
|
||||
const maxDuration = 360000; // Maximum duration set to 6 minutes
|
||||
const maxDuration = 360000 * 2; // Maximum duration set to 12 minutes
|
||||
const startTime = Date.now(); // Record the start time
|
||||
|
||||
// Promise to handle polling logic
|
||||
@ -379,6 +548,42 @@ async function listenForChatMessage({ nodeBaseUrl, senderAddress, senderPublicKe
|
||||
}
|
||||
}
|
||||
|
||||
async function listenForChatMessageForBuyOrder({ 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}`;
|
||||
const encodedMessageObj = await fetchMessages(apiCall)
|
||||
|
||||
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 decodedMessage = decryptChatMessage(encodedMessageObj.data, keyPair.privateKey, senderPublicKey, encodedMessageObj.reference)
|
||||
const parsedMessage = JSON.parse(decodedMessage)
|
||||
return parsedMessage
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request) {
|
||||
switch (request.action) {
|
||||
@ -600,7 +805,87 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "connection":
|
||||
case "buyOrder": {
|
||||
const { qortalAtAddress, hostname } = request.payload;
|
||||
getTradeInfo(qortalAtAddress)
|
||||
.then((crosschainAtInfo) => {
|
||||
const popupUrl = chrome.runtime.getURL("index.html");
|
||||
|
||||
chrome.windows.getAll(
|
||||
{ populate: true, windowTypes: ["popup"] },
|
||||
(windows) => {
|
||||
// Attempt to find an existing popup window that has a tab with the correct URL
|
||||
const existingPopup = windows.find(
|
||||
(w) =>
|
||||
w.tabs &&
|
||||
w.tabs.some(
|
||||
(tab) => tab.url && tab.url.startsWith(popupUrl)
|
||||
)
|
||||
);
|
||||
if (existingPopup) {
|
||||
// If the popup exists but is minimized or not focused, focus it
|
||||
chrome.windows.update(existingPopup.id, {
|
||||
focused: true,
|
||||
state: "normal",
|
||||
});
|
||||
} else {
|
||||
// No existing popup found, create a new one
|
||||
chrome.system.display.getInfo((displays) => {
|
||||
// Assuming the primary display is the first one (adjust logic as needed)
|
||||
const primaryDisplay = displays[0];
|
||||
const screenWidth = primaryDisplay.bounds.width;
|
||||
const windowHeight = 500; // Your window height
|
||||
const windowWidth = 400; // Your window width
|
||||
|
||||
// Calculate left position for the window to appear on the right of the screen
|
||||
const leftPosition = screenWidth - windowWidth;
|
||||
|
||||
// Calculate top position for the window, adjust as desired
|
||||
const topPosition =
|
||||
(primaryDisplay.bounds.height - windowHeight) / 2;
|
||||
|
||||
chrome.windows.create({
|
||||
url: chrome.runtime.getURL("index.html"),
|
||||
type: "popup",
|
||||
width: windowWidth,
|
||||
height: windowHeight,
|
||||
left: leftPosition,
|
||||
top: 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const interactionId = Date.now().toString(); // Simple example; consider a better unique ID
|
||||
|
||||
setTimeout(() => {
|
||||
chrome.runtime.sendMessage({
|
||||
action: "SET_COUNTDOWN",
|
||||
payload: request.timeout ? 0.9 * request.timeout : 20,
|
||||
});
|
||||
chrome.runtime.sendMessage({
|
||||
action: "UPDATE_STATE_REQUEST_BUY_ORDER",
|
||||
payload: {
|
||||
hostname,
|
||||
crosschainAtInfo,
|
||||
interactionId,
|
||||
},
|
||||
});
|
||||
}, 500);
|
||||
|
||||
// Store sendResponse callback with the interaction ID
|
||||
pendingResponses.set(interactionId, sendResponse);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
case "connection": {
|
||||
const { hostname } = request.payload;
|
||||
connection(hostname)
|
||||
.then((isConnected) => {
|
||||
@ -678,6 +963,8 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
.catch((error) => {
|
||||
console.error(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
case "sendQort":
|
||||
{
|
||||
@ -809,6 +1096,36 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
case "buyOrderConfirmation": {
|
||||
const { crosschainAtInfo, isDecline } = request.payload;
|
||||
const interactionId2 = request.payload.interactionId;
|
||||
// Retrieve the stored sendResponse callback
|
||||
const originalSendResponse = pendingResponses.get(interactionId2);
|
||||
|
||||
if (originalSendResponse) {
|
||||
if (isDecline) {
|
||||
originalSendResponse({ error: "User has declined" });
|
||||
sendResponse(false);
|
||||
pendingResponses.delete(interactionId2);
|
||||
return;
|
||||
}
|
||||
createBuyOrderTx({ crosschainAtInfo })
|
||||
.then((res) => {
|
||||
sendResponse(true);
|
||||
originalSendResponse(res);
|
||||
pendingResponses.delete(interactionId2);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error.message);
|
||||
sendResponse({ error: error.message });
|
||||
originalSendResponse({ error: error.message });
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
case "logout":
|
||||
{
|
||||
|
145
src/transactions/ChatBase.ts
Normal file
145
src/transactions/ChatBase.ts
Normal file
@ -0,0 +1,145 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import { QORT_DECIMALS, TX_TYPES } from '../constants/constants'
|
||||
import nacl from '../deps/nacl-fast'
|
||||
import Base58 from '../deps/Base58'
|
||||
import utils from '../utils/utils'
|
||||
|
||||
export default class ChatBase {
|
||||
static get utils() {
|
||||
return utils
|
||||
}
|
||||
|
||||
static get nacl() {
|
||||
return nacl
|
||||
}
|
||||
|
||||
static get Base58() {
|
||||
return Base58
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.fee = 0
|
||||
this.groupID = 0
|
||||
this.tests = [
|
||||
() => {
|
||||
if (!(this._type >= 1 && this._type in TX_TYPES)) {
|
||||
return 'Invalid type: ' + this.type
|
||||
}
|
||||
return true
|
||||
},
|
||||
() => {
|
||||
if (this._fee < 0) {
|
||||
return 'Invalid fee: ' + this._fee / QORT_DECIMALS
|
||||
}
|
||||
return true
|
||||
},
|
||||
() => {
|
||||
if (this._groupID < 0 || !Number.isInteger(this._groupID)) {
|
||||
return 'Invalid groupID: ' + this._groupID
|
||||
}
|
||||
return true
|
||||
},
|
||||
() => {
|
||||
if (!(new Date(this._timestamp)).getTime() > 0) {
|
||||
return 'Invalid timestamp: ' + this._timestamp
|
||||
}
|
||||
return true
|
||||
},
|
||||
() => {
|
||||
if (!(this._lastReference instanceof Uint8Array && this._lastReference.byteLength == 64)) {
|
||||
return 'Invalid last reference: ' + this._lastReference
|
||||
}
|
||||
return true
|
||||
},
|
||||
() => {
|
||||
if (!(this._keyPair)) {
|
||||
return 'keyPair must be specified'
|
||||
}
|
||||
if (!(this._keyPair.publicKey instanceof Uint8Array && this._keyPair.publicKey.byteLength === 32)) {
|
||||
return 'Invalid publicKey'
|
||||
}
|
||||
if (!(this._keyPair.privateKey instanceof Uint8Array && this._keyPair.privateKey.byteLength === 64)) {
|
||||
return 'Invalid privateKey'
|
||||
}
|
||||
return true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
set keyPair(keyPair) {
|
||||
this._keyPair = keyPair
|
||||
}
|
||||
|
||||
set type(type) {
|
||||
this.typeText = TX_TYPES[type]
|
||||
this._type = type
|
||||
this._typeBytes = this.constructor.utils.int32ToBytes(this._type)
|
||||
}
|
||||
|
||||
set groupID(groupID) {
|
||||
this._groupID = groupID
|
||||
this._groupIDBytes = this.constructor.utils.int32ToBytes(this._groupID)
|
||||
}
|
||||
|
||||
set timestamp(timestamp) {
|
||||
this._timestamp = timestamp
|
||||
this._timestampBytes = this.constructor.utils.int64ToBytes(this._timestamp)
|
||||
}
|
||||
|
||||
set fee(fee) {
|
||||
this._fee = fee * QORT_DECIMALS
|
||||
this._feeBytes = this.constructor.utils.int64ToBytes(this._fee)
|
||||
}
|
||||
|
||||
set lastReference(lastReference) {
|
||||
this._lastReference = lastReference instanceof Uint8Array ? lastReference : this.constructor.Base58.decode(lastReference)
|
||||
}
|
||||
|
||||
get params() {
|
||||
return [
|
||||
this._typeBytes,
|
||||
this._timestampBytes,
|
||||
this._groupIDBytes,
|
||||
this._lastReference,
|
||||
this._keyPair.publicKey
|
||||
]
|
||||
}
|
||||
|
||||
get chatBytes() {
|
||||
const isValid = this.validParams()
|
||||
if (!isValid.valid) {
|
||||
throw new Error(isValid.message)
|
||||
}
|
||||
|
||||
let result = new Uint8Array()
|
||||
|
||||
this.params.forEach(item => {
|
||||
result = this.constructor.utils.appendBuffer(result, item)
|
||||
})
|
||||
|
||||
this._chatBytes = result
|
||||
|
||||
return this._chatBytes
|
||||
}
|
||||
|
||||
validParams() {
|
||||
let finalResult = {
|
||||
valid: true
|
||||
}
|
||||
|
||||
this.tests.some(test => {
|
||||
const result = test()
|
||||
if (result !== true) {
|
||||
finalResult = {
|
||||
valid: false,
|
||||
message: result
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
return finalResult
|
||||
}
|
||||
|
||||
}
|
92
src/transactions/ChatTransaction.ts
Normal file
92
src/transactions/ChatTransaction.ts
Normal file
@ -0,0 +1,92 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import ChatBase from './ChatBase'
|
||||
import nacl from '../deps/nacl-fast'
|
||||
import ed2curve from '../deps/ed2curve'
|
||||
import { Sha256 } from 'asmcrypto.js'
|
||||
import { CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP } from '../constants/constants'
|
||||
|
||||
export default class ChatTransaction extends ChatBase {
|
||||
constructor() {
|
||||
super()
|
||||
this.type = 18
|
||||
this.fee = 0
|
||||
}
|
||||
|
||||
set recipientPublicKey(recipientPublicKey) {
|
||||
this._base58RecipientPublicKey = recipientPublicKey instanceof Uint8Array ? this.constructor.Base58.encode(recipientPublicKey) : recipientPublicKey
|
||||
this._recipientPublicKey = this.constructor.Base58.decode(this._base58RecipientPublicKey)
|
||||
}
|
||||
|
||||
set proofOfWorkNonce(proofOfWorkNonce) {
|
||||
this._proofOfWorkNonce = this.constructor.utils.int32ToBytes(proofOfWorkNonce)
|
||||
}
|
||||
|
||||
set recipient(recipient) {
|
||||
this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient)
|
||||
this._hasReceipient = new Uint8Array(1)
|
||||
this._hasReceipient[0] = 1
|
||||
}
|
||||
|
||||
set hasChatReference(hasChatReference) {
|
||||
this._hasChatReference = new Uint8Array(1)
|
||||
this._hasChatReference[0] = hasChatReference
|
||||
}
|
||||
|
||||
set chatReference(chatReference) {
|
||||
this._chatReference = chatReference instanceof Uint8Array ? chatReference : this.constructor.Base58.decode(chatReference)
|
||||
}
|
||||
|
||||
set message(message) {
|
||||
this.messageText = message;
|
||||
this._message = this.constructor.utils.stringtoUTF8Array(message)
|
||||
this._messageLength = this.constructor.utils.int32ToBytes(this._message.length)
|
||||
}
|
||||
|
||||
set isEncrypted(isEncrypted) {
|
||||
this._isEncrypted = new Uint8Array(1)
|
||||
this._isEncrypted[0] = isEncrypted
|
||||
|
||||
if (isEncrypted === 1) {
|
||||
const convertedPrivateKey = ed2curve.convertSecretKey(this._keyPair.privateKey)
|
||||
const convertedPublicKey = ed2curve.convertPublicKey(this._recipientPublicKey)
|
||||
const sharedSecret = new Uint8Array(32)
|
||||
nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedPublicKey)
|
||||
|
||||
this._chatEncryptionSeed = new Sha256().process(sharedSecret).finish().result
|
||||
this._encryptedMessage = nacl.secretbox(this._message, this._lastReference.slice(0, 24), this._chatEncryptionSeed)
|
||||
}
|
||||
|
||||
this._myMessage = isEncrypted === 1 ? this._encryptedMessage : this._message
|
||||
this._myMessageLenth = isEncrypted === 1 ? this.constructor.utils.int32ToBytes(this._myMessage.length) : this._messageLength
|
||||
}
|
||||
|
||||
set isText(isText) {
|
||||
this._isText = new Uint8Array(1)
|
||||
this._isText[0] = isText
|
||||
}
|
||||
|
||||
get params() {
|
||||
const params = super.params
|
||||
params.push(
|
||||
this._proofOfWorkNonce,
|
||||
this._hasReceipient,
|
||||
this._recipient,
|
||||
this._myMessageLenth,
|
||||
this._myMessage,
|
||||
this._isEncrypted,
|
||||
this._isText,
|
||||
this._feeBytes
|
||||
)
|
||||
|
||||
// After the feature trigger timestamp we need to include chat reference
|
||||
if (new Date(this._timestamp).getTime() >= CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP) {
|
||||
params.push(this._hasChatReference)
|
||||
|
||||
if (this._hasChatReference[0] == 1) {
|
||||
params.push(this._chatReference)
|
||||
}
|
||||
}
|
||||
return params
|
||||
}
|
||||
}
|
40
src/transactions/signChat.ts
Normal file
40
src/transactions/signChat.ts
Normal file
@ -0,0 +1,40 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import nacl from '../deps/nacl-fast'
|
||||
import utils from '../utils/utils'
|
||||
|
||||
export const signChat = (chatBytes, nonce, keyPair) => {
|
||||
|
||||
if (!chatBytes) {
|
||||
throw new Error('Chat Bytes not defined')
|
||||
}
|
||||
|
||||
if (!nonce) {
|
||||
throw new Error('Nonce not defined')
|
||||
}
|
||||
|
||||
if (!keyPair) {
|
||||
throw new Error('keyPair not defined')
|
||||
}
|
||||
|
||||
const _nonce = utils.int32ToBytes(nonce)
|
||||
|
||||
if (chatBytes.length === undefined) {
|
||||
const _chatBytesBuffer = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; })
|
||||
|
||||
const chatBytesBuffer = new Uint8Array(_chatBytesBuffer)
|
||||
chatBytesBuffer.set(_nonce, 112)
|
||||
|
||||
const signature = nacl.sign.detached(chatBytesBuffer, keyPair.privateKey)
|
||||
|
||||
return utils.appendBuffer(chatBytesBuffer, signature)
|
||||
} else {
|
||||
const chatBytesBuffer = new Uint8Array(chatBytes)
|
||||
chatBytesBuffer.set(_nonce, 112)
|
||||
|
||||
const signature = nacl.sign.detached(chatBytesBuffer, keyPair.privateKey)
|
||||
|
||||
return utils.appendBuffer(chatBytesBuffer, signature)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import PaymentTransaction from './PaymentTransaction.js'
|
||||
import ChatTransaction from './ChatTransaction.js'
|
||||
|
||||
|
||||
export const transactionTypes = {
|
||||
2: PaymentTransaction,
|
||||
|
||||
18: ChatTransaction
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user