Merge remote-tracking branch 'Phillip/feature/q-apps-confirmation-modals' into feature/qapps-modals

This commit is contained in:
Justin Ferrari 2023-02-24 17:49:17 -05:00
commit d90ad72d9b
7 changed files with 165 additions and 61 deletions

View File

@ -503,7 +503,9 @@
"bchange23": "Message Sent!", "bchange23": "Message Sent!",
"bchange24": "This shares your QORT address and allows your account to interact with the", "bchange24": "This shares your QORT address and allows your account to interact with the",
"bchange25": "No sensitive data is shared.", "bchange25": "No sensitive data is shared.",
"bchange26": "Always authenticate automatically" "bchange26": "Always authenticate automatically",
"bchange27": "Reject",
"bchange28": "Accept"
}, },
"datapage": { "datapage": {
"dchange1": "Data Management", "dchange1": "Data Management",

View File

@ -22,6 +22,7 @@ import framePasteMenu from '../functional-components/frame-paste-menu.js';
const createTransaction = api.createTransaction; const createTransaction = api.createTransaction;
const processTransaction = api.processTransaction; const processTransaction = api.processTransaction;
const processTransactionVersion2 = api.processTransactionVersion2;
const signChatTransaction = api.signChatTransaction; const signChatTransaction = api.signChatTransaction;
const signArbitraryTransaction = api.signArbitraryTransaction; const signArbitraryTransaction = api.signArbitraryTransaction;
const tradeBotCreateRequest = api.tradeBotCreateRequest; const tradeBotCreateRequest = api.tradeBotCreateRequest;
@ -145,7 +146,15 @@ export const routes = {
await requestTransactionDialog.requestTransaction(tx); await requestTransactionDialog.requestTransaction(tx);
} }
const res = await processTransaction(tx.signedBytes); let res
if(req.data.apiVersion && req.data.apiVersion === 2){
res = await processTransactionVersion2(tx.signedBytes)
}
if(!req.data.apiVersion){
res = await processTransaction(tx.signedBytes);
}
let extraData = {} let extraData = {}
if(req.data.type === 38 && tx && tx._rewardShareKeyPair && tx._rewardShareKeyPair.secretKey){ if(req.data.type === 38 && tx && tx._rewardShareKeyPair && tx._rewardShareKeyPair.secretKey){
extraData.rewardSharePrivateKey = Base58.encode(tx._rewardShareKeyPair.secretKey) extraData.rewardSharePrivateKey = Base58.encode(tx._rewardShareKeyPair.secretKey)
@ -191,7 +200,16 @@ export const routes = {
_keyPair, _keyPair,
req.data.params req.data.params
); );
const res = await processTransaction(tx.signedBytes); let res
if(req.data.apiVersion && req.data.apiVersion === 2){
res = await processTransactionVersion2(tx.signedBytes)
}
if(!req.data.apiVersion){
res = await processTransaction(tx.signedBytes);
}
response = { response = {
success: true, success: true,
data: res, data: res,
@ -243,7 +261,15 @@ export const routes = {
store.getState().app.wallet._addresses[req.data.nonce].keyPair store.getState().app.wallet._addresses[req.data.nonce].keyPair
); );
const res = await processTransaction(signedChatBytes); let res
if(req.data.apiVersion && req.data.apiVersion === 2){
res = await processTransactionVersion2(signedChatBytes)
}
if(!req.data.apiVersion){
res = await processTransaction(signedChatBytes);
}
response = res; response = res;
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -262,8 +288,15 @@ export const routes = {
req.data.arbitraryNonce, req.data.arbitraryNonce,
store.getState().app.wallet._addresses[req.data.nonce].keyPair store.getState().app.wallet._addresses[req.data.nonce].keyPair
); );
let res
if(req.data.apiVersion && req.data.apiVersion === 2){
res = await processTransactionVersion2(signedArbitraryBytes)
}
if(!req.data.apiVersion){
res = await processTransaction(signedArbitraryBytes);
}
const res = await processTransaction(signedArbitraryBytes);
response = res; response = res;
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -293,8 +326,14 @@ export const routes = {
unsignedTxn, unsignedTxn,
store.getState().app.selectedAddress.keyPair store.getState().app.selectedAddress.keyPair
); );
let res
const res = await processTransaction(signedTxnBytes); if(req.data.apiVersion && req.data.apiVersion === 2){
res = await processTransactionVersion2(signedTxnBytes)
}
if(!req.data.apiVersion){
res = await processTransaction(signedTxnBytes);
}
response = res; response = res;
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -328,7 +367,14 @@ export const routes = {
store.getState().app.selectedAddress.keyPair store.getState().app.selectedAddress.keyPair
); );
const res = await processTransaction(signedTxnBytes); let res
if(req.data.apiVersion && req.data.apiVersion === 2){
res = await processTransactionVersion2(signedTxnBytes)
}
if(!req.data.apiVersion){
res = await processTransaction(signedTxnBytes);
}
response = res; response = res;
} catch (e) { } catch (e) {

View File

@ -1,5 +1,5 @@
export { request } from './fetch-request.js' export { request } from './fetch-request.js'
export { transactionTypes as transactions } from './transactions/transactions.js' export { transactionTypes as transactions } from './transactions/transactions.js'
export { processTransaction, createTransaction, computeChatNonce, signChatTransaction, signArbitraryTransaction } from './createTransaction.js' export { processTransaction, processTransactionVersion2, createTransaction, computeChatNonce, signChatTransaction, signArbitraryTransaction } from './createTransaction.js'
export { tradeBotCreateRequest, tradeBotRespondRequest, signTradeBotTxn, deleteTradeOffer, sendBtc, sendLtc, sendDoge, sendDgb, sendRvn, sendArrr } from './tradeRequest.js' export { tradeBotCreateRequest, tradeBotRespondRequest, signTradeBotTxn, deleteTradeOffer, sendBtc, sendLtc, sendDoge, sendDgb, sendRvn, sendArrr } from './tradeRequest.js'
export { cancelAllOffers } from './transactions/trade-portal/tradeoffer/cancelAllOffers.js' export { cancelAllOffers } from './transactions/trade-portal/tradeoffer/cancelAllOffers.js'

View File

@ -36,3 +36,9 @@ export const processTransaction = bytes => request('/transactions/process', {
method: 'POST', method: 'POST',
body: Base58.encode(bytes) body: Base58.encode(bytes)
}) })
export const processTransactionVersion2 = bytes => request('/transactions/process?apiVersion=2', {
method: 'POST',
body: Base58.encode(bytes)
})

View File

@ -1,5 +1,6 @@
'use strict' 'use strict'
import TransactionBase from './TransactionBase.js' import TransactionBase from './TransactionBase.js'
import { store } from '../../api.js'
export default class DeployAtTransaction extends TransactionBase { export default class DeployAtTransaction extends TransactionBase {
constructor() { constructor() {
@ -33,7 +34,7 @@ export default class DeployAtTransaction extends TransactionBase {
this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) this._feeBytes = this.constructor.utils.int64ToBytes(this._fee)
} }
set rAmount(rAmount) { set rAmount(rAmount) {
this._rAmount = rAmount this._rAmount = Math.round(rAmount * store.getState().config.coin.decimals)
this._rAmountBytes = this.constructor.utils.int64ToBytes(this._rAmount) this._rAmountBytes = this.constructor.utils.int64ToBytes(this._rAmount)
} }
@ -60,7 +61,6 @@ export default class DeployAtTransaction extends TransactionBase {
} }
set rCreationBytes(rCreationBytes) { set rCreationBytes(rCreationBytes) {
const decode = this.constructor.Base58.decode(rCreationBytes) const decode = this.constructor.Base58.decode(rCreationBytes)
console.log({decode})
this._rCreationBytes = this.constructor.utils.stringtoUTF8Array(decode) this._rCreationBytes = this.constructor.utils.stringtoUTF8Array(decode)
this._rCreationBytesLength = this.constructor.utils.int32ToBytes(this._rCreationBytes.length) this._rCreationBytesLength = this.constructor.utils.int32ToBytes(this._rCreationBytes.length)
} }

View File

@ -295,7 +295,20 @@ class WebBrowser extends LitElement {
} }
const data = await response.json() const data = await response.json()
const joinFee = data const joinFee = (Number(data) / 1e8).toFixed(8)
return joinFee
}
async sendQortFee() {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
const url = `${nodeUrl}/transactions/unitfee?txType=PAYMENT`
const response = await fetch(url)
if (!response.ok) {
throw new Error('Error when fetching join fee');
}
const data = await response.json()
const joinFee = (Number(data) / 1e8).toFixed(8)
return joinFee return joinFee
} }
@ -330,7 +343,8 @@ class WebBrowser extends LitElement {
lastReference: lastRef, lastReference: lastRef,
groupdialog1: groupdialog1, groupdialog1: groupdialog1,
groupdialog2: groupdialog2 groupdialog2: groupdialog2
} },
apiVersion: 2
}) })
return myTxnrequest return myTxnrequest
} }
@ -386,7 +400,8 @@ class WebBrowser extends LitElement {
lastReference: lastRef, lastReference: lastRef,
atDeployDialog1: groupdialog1, atDeployDialog1: groupdialog1,
atDeployDialog2: groupdialog2 atDeployDialog2: groupdialog2
} },
apiVersion: 2
}) })
return myTxnrequest return myTxnrequest
} }
@ -452,10 +467,8 @@ class WebBrowser extends LitElement {
let response = '{"error": "Request could not be fulfilled"}'; let response = '{"error": "Request could not be fulfilled"}';
let data = event.data; let data = event.data;
console.log('UI received event: ' + JSON.stringify(data));
switch (data.action) { switch (data.action) {
case 'GET_USER_ACCOUNT':
case actions.GET_USER_ACCOUNT: case actions.GET_USER_ACCOUNT:
let skip = false; let skip = false;
if (window.parent.reduxStore.getState().app.qAPPAutoAuth) { if (window.parent.reduxStore.getState().app.qAPPAutoAuth) {
@ -480,12 +493,12 @@ class WebBrowser extends LitElement {
break; break;
} else { } else {
const data = {}; const data = {};
const errorMsg = get('browserpage.bchange17'); const errorMsg = "User declined to share account details"
data['error'] = errorMsg; data['error'] = errorMsg;
response = JSON.stringify(data); response = JSON.stringify(data);
break; break;
} }
case 'LINK_TO_QDN_RESOURCE': case actions.LINK_TO_QDN_RESOURCE:
case actions.QDN_RESOURCE_DISPLAYED: case actions.QDN_RESOURCE_DISPLAYED:
// Links are handled by the core, but the UI also listens for these actions in order to update the address bar. // Links are handled by the core, but the UI also listens for these actions in order to update the address bar.
// Note: don't update this.url here, as we don't want to force reload the iframe each time. // Note: don't update this.url here, as we don't want to force reload the iframe each time.
@ -551,6 +564,7 @@ class WebBrowser extends LitElement {
selectedAddress: this.selectedAddress, selectedAddress: this.selectedAddress,
worker: worker, worker: worker,
isBase64: true, isBase64: true,
apiVersion: 2
}); });
response = JSON.stringify(resPublish); response = JSON.stringify(resPublish);
@ -577,11 +591,11 @@ class WebBrowser extends LitElement {
} }
case 'SEND_CHAT_MESSAGE': { case actions.SEND_CHAT_MESSAGE: {
const message = data.message; const message = data.message;
const recipient = data.destinationAddress; const recipient = data.destinationAddress;
const sendMessage = async (messageText, chatReference) => { const sendMessage = async (messageText, chatReference) => {
this.loader.show();
let _reference = new Uint8Array(64); let _reference = new Uint8Array(64);
window.crypto.getRandomValues(_reference); window.crypto.getRandomValues(_reference);
let reference = window.parent.Base58.encode(_reference); let reference = window.parent.Base58.encode(_reference);
@ -625,7 +639,8 @@ class WebBrowser extends LitElement {
let _response = await parentEpml.request('sign_chat', { let _response = await parentEpml.request('sign_chat', {
nonce: this.selectedAddress.nonce, nonce: this.selectedAddress.nonce,
chatBytesArray: chatBytesArray, chatBytesArray: chatBytesArray,
chatNonce: nonce chatNonce: nonce,
apiVersion: 2
}); });
const chatResponse = getSendChatResponse(_response); const chatResponse = getSendChatResponse(_response);
@ -634,13 +649,13 @@ class WebBrowser extends LitElement {
const getSendChatResponse = (res) => { const getSendChatResponse = (res) => {
if (res === true) { if (res === true) {
let successString = get("browserpage.bchange23"); return res
parentEpml.request('showSnackBar', `${successString}`);
} else if (res.error) { } else if (res.error) {
parentEpml.request('showSnackBar', res.message); throw new Error(res.message);
} else {
throw new Error('ERROR: Could not send message');
} }
this.loader.hide();
return res;
}; };
const chatResponse = await sendMessageRequest(); const chatResponse = await sendMessageRequest();
@ -676,7 +691,7 @@ class WebBrowser extends LitElement {
return return
} }
this.loader.show();
const tiptapJson = { const tiptapJson = {
type: 'doc', type: 'doc',
@ -710,14 +725,15 @@ class WebBrowser extends LitElement {
// this.sendMessage(stringifyMessageObject, typeMessage); // this.sendMessage(stringifyMessageObject, typeMessage);
// } // }
try { try {
this.loader.show();
const msgResponse = await sendMessage(stringifyMessageObject); const msgResponse = await sendMessage(stringifyMessageObject);
response = msgResponse; response = msgResponse;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return '{"error": "Request could not be fulfilled"}'; response = '{"error": "Request could not be fulfilled"}';
} finally { } finally {
this.loader.hide(); this.loader.hide();
console.log("Case completed.");
} }
} else { } else {
@ -831,7 +847,7 @@ class WebBrowser extends LitElement {
// } // }
case 'GET_WALLET_BALANCE': { case actions.GET_WALLET_BALANCE: {
const requiredFields = ['coin']; const requiredFields = ['coin'];
const missingFields = []; const missingFields = [];
@ -942,23 +958,43 @@ class WebBrowser extends LitElement {
} }
case 'SEND_COIN': case actions.SEND_COIN: {
const requiredFields = ['coin', 'destinationAddress', 'amount'];
const missingFields = [];
requiredFields.forEach((field) => {
if (!data[field]) {
missingFields.push(field);
}
});
if (missingFields.length > 0) {
this.loader.hide();
const missingFieldsString = missingFields.join(', ');
const errorMsg = `Missing fields: ${missingFieldsString}`
let data = {};
data['error'] = errorMsg;
response = JSON.stringify(data);
break
}
// Params: data.coin, data.destinationAddress, data.amount, data.fee // Params: data.coin, data.destinationAddress, data.amount, data.fee
// TODO: prompt user to send. If they confirm, call `POST /crosschain/:coin/send`, or for QORT, broadcast a PAYMENT transaction // TODO: prompt user to send. If they confirm, call `POST /crosschain/:coin/send`, or for QORT, broadcast a PAYMENT transaction
// then set the response string from the core to the `response` variable (defined above) // then set the response string from the core to the `response` variable (defined above)
// If they decline, send back JSON that includes an `error` key, such as `{"error": "User declined request"}` // If they decline, send back JSON that includes an `error` key, such as `{"error": "User declined request"}`
const amount = data.amount const amount = Number(data.amount)
let recipient = data.destinationAddress; let recipient = data.destinationAddress;
const fee = data.fee
this.loader.show(); this.loader.show();
const walletBalance = await parentEpml.request('apiCall', { const walletBalance = await parentEpml.request('apiCall', {
url: `/addresses/balance/${this.myAddress.address}?apiKey=${this.getApiKey()}`, url: `/addresses/balance/${this.myAddress.address}?apiKey=${this.getApiKey()}`,
}) })
if (isNaN(Number(walletBalance))) { if (isNaN(Number(walletBalance))) {
let snack4string = get("chatpage.cchange48") this.loader.hide();
parentEpml.request('showSnackBar', `${snack4string}`) let errorMsg = "Failed to Fetch QORT Balance. Try again!"
return; let obj = {};
obj['error'] = errorMsg;
response = JSON.stringify(obj);
break;
} }
const myRef = await parentEpml.request("apiCall", { const myRef = await parentEpml.request("apiCall", {
@ -966,27 +1002,35 @@ class WebBrowser extends LitElement {
url: `/addresses/lastreference/${this.myAddress.address}`, url: `/addresses/lastreference/${this.myAddress.address}`,
}) })
const walletBalanceDecimals = parseFloat(walletBalance) * QORT_DECIMALS; const walletBalanceDecimals = Number(walletBalance) * QORT_DECIMALS;
const amountDecimals = Number(amount) * QORT_DECIMALS
if (parseFloat(amount) + parseFloat(data.fee) > parseFloat(walletBalanceDecimals)) { const fee = await this.sendQortFee()
// TODO fee
if (amountDecimals + (fee * QORT_DECIMALS) > walletBalanceDecimals) {
this.loader.hide(); this.loader.hide();
let snack1string = get("chatpage.cchange51"); let errorMsg = "Insufficient Funds!"
parentEpml.request('showSnackBar', `${snack1string}`); let obj = {};
return false; obj['error'] = errorMsg;
response = JSON.stringify(obj);
break;
} }
if (parseFloat(amount) <= 0) { if (amount <= 0) {
this.loader.hide(); this.loader.hide();
let snack2string = get("chatpage.cchange52"); let errorMsg = "Invalid Amount!"
parentEpml.request('showSnackBar', `${snack2string}`); let obj = {};
return false; obj['error'] = errorMsg;
response = JSON.stringify(obj);
break;
} }
if (recipient.length === 0) { if (recipient.length === 0) {
this.loader.hide(); this.loader.hide();
let snack3string = get("chatpage.cchange53"); let errorMsg = "Receiver cannot be empty!"
parentEpml.request('showSnackBar', `${snack3string}`); let obj = {};
return false; obj['error'] = errorMsg;
response = JSON.stringify(obj);
break;
} }
const validateName = async (receiverName) => { const validateName = async (receiverName) => {
@ -1031,9 +1075,10 @@ class WebBrowser extends LitElement {
const res = getTxnRequestResponse(myTransaction) const res = getTxnRequestResponse(myTransaction)
return res; return res;
} else { } else {
console.error(`${translate("chatpage.cchange54")}`)
parentEpml.request('showSnackBar', `${translate("chatpage.cchange54")}`) let errorMsg = "Invalid Receiver!"
this.loader.hide(); throw new Error(errorMsg)
} }
} }
} }
@ -1077,6 +1122,7 @@ class WebBrowser extends LitElement {
dialogAddress, dialogAddress,
dialogName dialogName
}, },
apiVersion: 2
}) })
return myTxnrequest; return myTxnrequest;
} }
@ -1084,14 +1130,15 @@ class WebBrowser extends LitElement {
const getTxnRequestResponse = (txnResponse) => { const getTxnRequestResponse = (txnResponse) => {
if (txnResponse.success === false && txnResponse.message) { if (txnResponse.success === false && txnResponse.message) {
this.loader.hide(); this.loader.hide();
throw new Error(txnResponse); throw new Error(txnResponse.message);
} else if (txnResponse.success === true && !txnResponse.data.error) { } else if (txnResponse.success === true && !txnResponse.data.error) {
this.loader.hide(); this.loader.hide();
return txnResponse.data;
} else { } else {
this.loader.hide(); this.loader.hide();
throw new Error(txnResponse); throw new Error('Error: could not send coin');
} }
return txnResponse;
} }
try { try {
@ -1099,11 +1146,13 @@ class WebBrowser extends LitElement {
response = result; response = result;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return '{"error": "Request could not be fulfilled"}'; response = '{"error": "Request could not be fulfilled"}';
} finally { } finally {
console.log("Case completed."); this.loader.hide();
} }
break; break;
}
default: default:
console.log('Unhandled message: ' + JSON.stringify(data)); console.log('Unhandled message: ' + JSON.stringify(data));
@ -1118,7 +1167,6 @@ class WebBrowser extends LitElement {
// Not all responses will be JSON // Not all responses will be JSON
responseObj = response; responseObj = response;
} }
// Respond to app // Respond to app
if (responseObj.error != null) { if (responseObj.error != null) {
event.ports[0].postMessage({ event.ports[0].postMessage({
@ -1470,8 +1518,8 @@ async function showModalAndWait(type, data) {
${type === actions.SEND_CHAT_MESSAGE ? `<p class="modal-paragraph">${get("browserpage.bchange22")}</p>` : ''} ${type === actions.SEND_CHAT_MESSAGE ? `<p class="modal-paragraph">${get("browserpage.bchange22")}</p>` : ''}
</div> </div>
<div class="modal-buttons"> <div class="modal-buttons">
<button id="cancel-button">Reject</button> <button id="cancel-button">${get("browserpage.bchange27")}</button>
<button id="ok-button">Accept</button> <button id="ok-button">${get("browserpage.bchange28")}</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -18,7 +18,8 @@ export const publishData = async ({
selectedAddress, selectedAddress,
worker, worker,
isBase64, isBase64,
metaData metaData,
apiVersion
}) => { }) => {
const validateName = async (receiverName) => { const validateName = async (receiverName) => {
let nameRes = await parentEpml.request("apiCall", { let nameRes = await parentEpml.request("apiCall", {
@ -71,6 +72,7 @@ export const publishData = async ({
arbitraryBytesBase58: transactionBytesBase58, arbitraryBytesBase58: transactionBytesBase58,
arbitraryBytesForSigningBase58: convertedBytesBase58, arbitraryBytesForSigningBase58: convertedBytesBase58,
arbitraryNonce: nonce, arbitraryNonce: nonce,
apiVersion: apiVersion ? apiVersion : null
}) })
let myResponse = { error: "" } let myResponse = { error: "" }
if (response === false) { if (response === false) {