diff --git a/.electron-builder.config.cjs b/.electron-builder.config.cjs
index a0f463e4..ef910154 100644
--- a/.electron-builder.config.cjs
+++ b/.electron-builder.config.cjs
@@ -9,7 +9,9 @@
* @see https://www.electron.build/configuration/configuration
*/
module.exports = async function () {
- const {getVersion} = await import('./version/getVersion.mjs');
+ const {
+ getVersion
+ } = await import('./version/getVersion.mjs');
return {
directories: {
@@ -23,7 +25,27 @@ module.exports = async function () {
productName: "Clorio Wallet",
// Specify linux target just for disabling snap compilation
linux: {
- target: 'deb',
+ target: [{
+ target: "deb",
+ },
+ {
+ target: "AppImage",
+ },
+ ]
+ },
+ dmg: {
+ contents: [{
+ x: 340,
+ y: 270,
+ type: 'file',
+ },
+ {
+ x: 560,
+ y: 270,
+ type: 'link',
+ path: '/Applications',
+ },
+ ],
},
};
};
diff --git a/build/background.png b/build/background.png
new file mode 100644
index 00000000..cbb1ae53
Binary files /dev/null and b/build/background.png differ
diff --git a/buildResources/background.png b/buildResources/background.png
new file mode 100644
index 00000000..cbb1ae53
Binary files /dev/null and b/buildResources/background.png differ
diff --git a/package.json b/package.json
index 07ab2ba5..f0233f2d 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "clorio-wallet",
"description": "Clorio wallet",
- "version": "2.1.1",
+ "version": "2.1.2",
"private": true,
"main": "packages/main/dist/index.cjs",
"author": {
@@ -19,6 +19,7 @@
"compile:mac": "yarn build && ./node_modules/.bin/electron-builder --config .electron-builder.config.cjs --publish never",
"compile:all": "CSC_IDENTITY_AUTO_DISCOVERY=false yarn build && ./node_modules/.bin/electron-builder -mwl --config .electron-builder.config.cjs --publish never",
"compile:linux": "CSC_IDENTITY_AUTO_DISCOVERY=false yarn build && ./node_modules/.bin/electron-builder -l --config .electron-builder.config.cjs --publish never",
+ "compile:ml": "CSC_IDENTITY_AUTO_DISCOVERY=false yarn build && ./node_modules/.bin/electron-builder -ml --config .electron-builder.config.cjs --publish never",
"test": "npm run test:main && npm run test:preload && npm run test:renderer && npm run test:e2e",
"test:e2e": "npm run build && vitest run",
"test:main": "vitest run -r packages/main --passWithNoTests",
diff --git a/packages/renderer/src/App.scss b/packages/renderer/src/App.scss
index 9ef17581..77dc0dda 100644
--- a/packages/renderer/src/App.scss
+++ b/packages/renderer/src/App.scss
@@ -9,6 +9,7 @@
@import "./styles/__new_design";
@import "./styles/__accountAvatar";
@import "./styles/__sidebar";
+@import "./styles/__zkapps";
@import url("https://fonts.googleapis.com/css2?family=Mada:wght@200;300;400;500;600;700;900&display=swap");
a {
diff --git a/packages/renderer/src/components/UI/epochBar/EpochBar.tsx b/packages/renderer/src/components/UI/epochBar/EpochBar.tsx
index 78b2a8fa..fdace3e7 100644
--- a/packages/renderer/src/components/UI/epochBar/EpochBar.tsx
+++ b/packages/renderer/src/components/UI/epochBar/EpochBar.tsx
@@ -13,6 +13,7 @@ interface RemainingTime {
const EpochBar = () => {
const {settings} = useNetworkSettingsContext();
+ const [epochError, setEpochError] = useState(false);
const [epochData, setEpochData] = useState({
slot: 0,
epoch: 0,
@@ -42,17 +43,21 @@ const EpochBar = () => {
}, []);
const fetchEpochData = async () => {
- const data = await fetch(settings?.epochUrl || '')
- .then(response => response.json())
- .then(data => data);
- const realSlot = +data.slot % (MAX_SLOT + 1);
- const lastTime = ((MAX_SLOT - +realSlot) * SLOT_DURATION) / 1000;
- setEpochData(data);
- setRemainingTime(calculateTime(lastTime));
- setPercentage(parseInt(((100 * +realSlot) / MAX_SLOT).toFixed(0)));
+ try {
+ const data = await fetch(settings?.epochUrl || '')
+ .then(response => response.json())
+ .then(data => data);
+ const realSlot = +data.slot % (MAX_SLOT + 1);
+ const lastTime = ((MAX_SLOT - +realSlot) * SLOT_DURATION) / 1000;
+ setEpochData(data);
+ setRemainingTime(calculateTime(lastTime));
+ setPercentage(parseInt(((100 * +realSlot) / MAX_SLOT).toFixed(0)));
+ } catch (error) {
+ setEpochError(true);
+ }
};
- if (!settings?.epochUrl) {
+ if (!settings?.epochUrl || epochError) {
return (
{
+ fetchAndSetNonce();
+ }, [showDelegationConfirmation]);
+
useEffect(() => {
if (fromRef.current) {
setFromTextWidth(fromRef.current.offsetWidth - 250);
@@ -67,6 +73,21 @@ export default function ConfirmZkappDelegation() {
sendResponse('clorio-error', ERROR_CODES.userRejectedRequest);
};
+ const fetchAndSetNonce = async () => {
+ if (showDelegationConfirmation) {
+ const {data: nonceData} = await fetchNonce({variables: {publicKey: wallet.address}});
+ if (nonceData?.accountByKey?.usableNonce || nonceData?.accountByKey?.usableNonce === 0) {
+ setZkappState(state => ({
+ ...state,
+ transactionData: {
+ ...state.transactionData,
+ nonce: nonceData.accountByKey.usableNonce,
+ },
+ }));
+ }
+ }
+ };
+
// Check with BigJs if the balance is enough
const checkBalance = (fee: number | string) => {
if (getBalance) {
@@ -110,18 +131,10 @@ export default function ConfirmZkappDelegation() {
sendResponse('error', {error: nonceError});
return;
}
- // TODO: Add custom nonce
- // Get the nonce
-
- const nonceResponse = await fetchNonce({variables: {publicKey: address[0]}});
- const nonce = nonceResponse?.data?.accountByKey?.usableNonce;
- if (!nonce && nonce !== 0) {
- throw new Error('Nonce not found');
- }
// Prepare the stake data
const stakeData = {
...transactionData,
- nonce,
+ // nonce,
from: address[0],
fee: toNanoMINA(transactionData.fee),
};
@@ -129,19 +142,19 @@ export default function ConfirmZkappDelegation() {
const clientInstance = await client();
const signedTx = await clientInstance.signStakeDelegation(stakeData, privateKey);
const hashedTx = await clientInstance.hashStakeDelegation(signedTx);
- await broadcastDelegationTx(signedTx);
- sendResponse('clorio-staked-delegation', {hash: hashedTx});
- onPostSign();
+ await broadcastDelegationTx(signedTx, hashedTx);
} catch (error) {
console.error('Error in onConfirm:', error);
toast.error('An error occurred while confirming the transaction');
}
};
- const broadcastDelegationTx = async (signedTx: SignedTx) => {
+ const broadcastDelegationTx = async (signedTx: SignedTx, hashedTx?: any) => {
await broadcastDelegation({
variables: {input: signedTx.data, signature: signedTx.signature},
});
+ sendResponse('clorio-staked-delegation', {hash: hashedTx || ''});
+ onPostSign();
};
const onPostSign = () => {
@@ -186,10 +199,17 @@ export default function ConfirmZkappDelegation() {
{showPassword ? (
- setShowPassword(false)}
- onSuccess={onConfirm}
- />
+ isLedgerEnabled ? (
+ setShowPassword(false)}
+ onSuccess={broadcastDelegationTx}
+ />
+ ) : (
+ setShowPassword(false)}
+ onSuccess={onConfirm}
+ />
+ )
) : (
void;
+ onSuccess: (signedTx: unknown) => void;
onClose: () => void;
}
export default function ConfirmZkappLedger({onSuccess, onClose}: IConfirmZkappLedger) {
@@ -31,7 +31,6 @@ export default function ConfirmZkappLedger({onSuccess, onClose}: IConfirmZkappLe
*/
const sendLedgerTransaction = async () => {
try {
- checkMemoLength(transactionData.memo);
await isMinaAppOpen();
const senderAccount = ledgerAccount || 0;
const signature = await createAndSignLedgerTransaction({
@@ -47,7 +46,7 @@ export default function ConfirmZkappLedger({onSuccess, onClose}: IConfirmZkappLe
},
nonce: +transactionData.nonce || 0,
});
- completeSignature(reEncodeRawSignature(signature.signature));
+ completeSignature(signature.signature);
} catch (e) {
setShowError(true);
setErrorMessage(e.message || 'An error occurred while loading hardware wallet');
@@ -57,15 +56,15 @@ export default function ConfirmZkappLedger({onSuccess, onClose}: IConfirmZkappLe
const completeSignature = signature => {
const data = {
- signature,
+ rawSignature: signature,
publicKey: transactionData.from,
data: {
from: transactionData.from,
to: transactionData.to,
- amount: toNanoMINA(transactionData.amount),
- fee: toNanoMINA(transactionData.fee),
+ amount: '' + toNanoMINA(transactionData.amount),
+ fee: '' + toNanoMINA(transactionData.fee),
memo: transactionData.memo,
- nonce: +transactionData.nonce || 0,
+ nonce: '' + transactionData.nonce || 0,
},
};
return onSuccess(data);
diff --git a/packages/renderer/src/components/UI/modals/zkAppIntegration/ConfirmZkappLedgerDelegation.tsx b/packages/renderer/src/components/UI/modals/zkAppIntegration/ConfirmZkappLedgerDelegation.tsx
new file mode 100644
index 00000000..c310df10
--- /dev/null
+++ b/packages/renderer/src/components/UI/modals/zkAppIntegration/ConfirmZkappLedgerDelegation.tsx
@@ -0,0 +1,88 @@
+import {useEffect, useState} from 'react';
+import {useRecoilValue} from 'recoil';
+import {walletState, zkappState} from '/@/store';
+import {
+ createLedgerDelegationTransaction,
+ isMinaAppOpen,
+ signTransaction,
+} from '/@/tools/ledger/ledger';
+import {toast} from 'react-toastify';
+import {toNanoMINA} from '/@/tools';
+import LedgerSignError from './LedgerSignError';
+import LedgerSingPending from './LedgerSingPending';
+
+interface IConfirmZkappLedgerDelegation {
+ onSuccess: (signedTx: unknown) => void;
+ onClose: () => void;
+}
+export default function ConfirmZkappLedgerDelegation({
+ onSuccess,
+ onClose,
+}: IConfirmZkappLedgerDelegation) {
+ const {transactionData} = useRecoilValue(zkappState);
+ const {ledgerAccount} = useRecoilValue(walletState);
+ const [showError, setShowError] = useState(false);
+ const [errorMessage, setErrorMessage] = useState('');
+
+ useEffect(() => {
+ sendLedgerTransaction();
+ }, []);
+
+ /**
+ * Sign transaction with Ledger
+ */
+ const sendLedgerTransaction = async () => {
+ try {
+ await isMinaAppOpen();
+ const senderAccount = ledgerAccount || 0;
+ const transactionToSend = await createLedgerDelegationTransaction({
+ senderAddress: transactionData.from,
+ receiverAddress: transactionData.to,
+ fee: toNanoMINA(transactionData.fee),
+ nonce: +transactionData.nonce || 0,
+ senderAccount,
+ });
+
+ const signature = await signTransaction(transactionToSend);
+ completeSignature(signature.signature);
+ } catch (e) {
+ console.log('🚀 ~ sendLedgerTransaction ~ e:', e);
+ setShowError(true);
+ setErrorMessage(e.message || 'An error occurred while loading hardware wallet');
+ toast.error(e.message || 'An error occurred while loading hardware wallet');
+ }
+ };
+
+ const completeSignature = signature => {
+ const data = {
+ signature: {rawSignature: signature},
+ publicKey: transactionData.from,
+ data: {
+ from: transactionData.from,
+ to: transactionData.to,
+ fee: '' + toNanoMINA(transactionData.fee),
+ nonce: '' + transactionData.nonce || 0,
+ },
+ };
+ return onSuccess(data);
+ };
+
+ return (
+
+
+
+
+ {showError ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+}
diff --git a/packages/renderer/src/components/UI/modals/zkAppIntegration/ConfirmZkappPayment.tsx b/packages/renderer/src/components/UI/modals/zkAppIntegration/ConfirmZkappPayment.tsx
index fe5b111c..8899a198 100644
--- a/packages/renderer/src/components/UI/modals/zkAppIntegration/ConfirmZkappPayment.tsx
+++ b/packages/renderer/src/components/UI/modals/zkAppIntegration/ConfirmZkappPayment.tsx
@@ -12,7 +12,12 @@ import {signTransaction} from '../../../../tools/utils';
import PasswordDecrypt from '../../../PasswordDecrypt';
import {toast} from 'react-toastify';
import {mnemonicToPrivateKey} from '../../../../../../preload/src/bip';
-import {client, createPaymentInputFromPayload, createSignatureInputFromSignature, toNanoMINA} from '/@/tools';
+import {
+ client,
+ createPaymentInputFromPayload,
+ createSignatureInputFromSignature,
+ toNanoMINA,
+} from '/@/tools';
import {ERROR_CODES} from '/@/tools/zkapp';
import ConfirmZkappLedger from './ConfirmZkappLedger';
import TransactionData from './TransactionData';
@@ -39,11 +44,23 @@ export default function ConfirmZkappPayment() {
});
useEffect(() => {
+ fetchAndSetNonce();
+ }, [showPaymentConfirmation]);
+
+ const fetchAndSetNonce = async () => {
if (showPaymentConfirmation) {
- const address = getAccountAddress();
- fetchNonce({variables: {publicKey: address[0]}});
+ const {data: nonceData} = await fetchNonce({variables: {publicKey: wallet.address}});
+ if (nonceData?.accountByKey?.usableNonce || nonceData?.accountByKey?.usableNonce === 0) {
+ setZkappState(state => ({
+ ...state,
+ transactionData: {
+ ...state.transactionData,
+ nonce: nonceData.accountByKey.usableNonce,
+ },
+ }));
+ }
}
- }, [showPaymentConfirmation]);
+ };
const onClose = () => {
setZkappState(zkappInitialState);
@@ -57,7 +74,7 @@ export default function ConfirmZkappPayment() {
const address = getAccountAddress();
const balance = getBalance(address[0]);
const available = +(balance?.liquidUnconfirmed || 0);
- if (+available > 0 && (+Big(available).sub(fee)) >= 0) {
+ if (+available > 0 && +Big(available).sub(fee) >= 0) {
return true;
}
}
@@ -76,10 +93,8 @@ export default function ConfirmZkappPayment() {
sendResponse('error', {error: nonceError});
return;
}
- const nonce = nonceData?.accountByKey?.usableNonce;
const signedTx = await signTransaction(privateKey, {
...transactionData,
- nonce,
from: address[0],
amount: toNanoMINA(transactionData.amount),
fee: toNanoMINA(transactionData.fee),
@@ -88,8 +103,13 @@ export default function ConfirmZkappPayment() {
};
const completePayment = async (signedTx: unknown) => {
- const hashedTx = await (await client()).hashPayment(signedTx);
- sendResponse('clorio-signed-payment', {hash: hashedTx});
+ if (isLedgerEnabled) {
+ // TODO: Implement ledger hash from BE
+ sendResponse('clorio-signed-payment', {hash: ''});
+ } else {
+ const hashedTx = await (await client()).hashPayment(signedTx);
+ sendResponse('clorio-signed-payment', {hash: hashedTx});
+ }
toast.success('Transaction signed successfully, waiting for broadcast.');
setZkappState(state => ({
...state,
@@ -101,7 +121,12 @@ export default function ConfirmZkappPayment() {
};
const broadcastPayment = async (signedTx: unknown) => {
- const signatureInput = createSignatureInputFromSignature(signedTx.signature);
+ let signatureInput;
+ if (isLedgerEnabled) {
+ signatureInput = {rawSignature: signedTx.rawSignature};
+ } else {
+ signatureInput = createSignatureInputFromSignature(signedTx.signature);
+ }
const paymentInput = createPaymentInputFromPayload(signedTx.data);
await broadcastTransaction({
variables: {input: paymentInput, signature: signatureInput},
diff --git a/packages/renderer/src/components/UI/modals/zkAppIntegration/LedgerSingPending.tsx b/packages/renderer/src/components/UI/modals/zkAppIntegration/LedgerSingPending.tsx
index edac503b..959ad4ab 100644
--- a/packages/renderer/src/components/UI/modals/zkAppIntegration/LedgerSingPending.tsx
+++ b/packages/renderer/src/components/UI/modals/zkAppIntegration/LedgerSingPending.tsx
@@ -1,10 +1,19 @@
import TransactionData from './TransactionData';
-export default function LedgerSingPending({transactionData}: {transactionData: any}) {
+export default function LedgerSingPending({
+ transactionData,
+ isDelegation,
+}: {
+ transactionData: any;
+ isDelegation?: boolean;
+}) {
return (
<>
-
+
Waiting for the Ledger device to sign the transaction.
diff --git a/packages/renderer/src/components/UI/modals/zkAppIntegration/ZkappConnectedApps.tsx b/packages/renderer/src/components/UI/modals/zkAppIntegration/ZkappConnectedApps.tsx
new file mode 100644
index 00000000..5fa575d1
--- /dev/null
+++ b/packages/renderer/src/components/UI/modals/zkAppIntegration/ZkappConnectedApps.tsx
@@ -0,0 +1,106 @@
+import {useState} from 'react';
+import {AlertOctagon} from 'react-feather';
+import Hoc from '../../Hoc';
+import Button from '../../Button';
+import {useRecoilValue} from 'recoil';
+import {connectedSitesState} from '/@/store';
+import NewZkappConnectionModal from './NewZkappConnectionModal';
+import Input from '../../input/Input';
+import {toast} from 'react-toastify';
+const GOOGLE_FAVICON_URL = 'https://s2.googleusercontent.com/s2/favicons?domain_url=';
+
+const initialZkappData = {
+ name: '',
+ url: '',
+};
+
+export const ZkappConnectedApps = () => {
+ const [showNewZkapp, setShowNewZkapp] = useState(false);
+ const {sites} = useRecoilValue(connectedSitesState);
+ const [newZkapp, setNewZkapp] = useState(initialZkappData);
+
+ const openLink = (url: string) => {
+ (window.ipcBridge as any).invoke('open-win', JSON.stringify({url}));
+ };
+
+ const isValidUrl = () => {
+ try {
+ new URL(newZkapp.url);
+ return true;
+ } catch (e) {
+ return false;
+ }
+ };
+
+ const onSubmit = () => {
+ if (!isValidUrl()) {
+ toast.info('Invalid URL');
+ return;
+ }
+ openLink(newZkapp.url);
+ setNewZkapp(initialZkappData);
+ setShowNewZkapp(false);
+ };
+
+ return (
+
+
+
+
+ Connect only to trusted Zkapps.
+ Do not enter your private keys on untrusted sites or suspicious sites.
+
+
+
+
+
Open Zkapp
+
+ {
+ setNewZkapp({...newZkapp, url: e.target.value});
+ }}
+ />
+
+
+
+
Connected Zkapps
+
+ {sites.map(({source, title}: {source: string; title: string}) => (
+
openLink(source)}
+ >
+
+
{title}
+
{source}
+
+ ))}
+
+
+
+
+
+ );
+};
diff --git a/packages/renderer/src/components/UI/sidebar/NetworkSettings.tsx b/packages/renderer/src/components/UI/sidebar/NetworkSettings.tsx
index d47acc00..085a6978 100644
--- a/packages/renderer/src/components/UI/sidebar/NetworkSettings.tsx
+++ b/packages/renderer/src/components/UI/sidebar/NetworkSettings.tsx
@@ -12,6 +12,7 @@ import {useRecoilState} from 'recoil';
import {networkState} from '/@/store';
import {ConnectedZkapps} from './ConnectedZkapps';
import {NetConfig, sendResponse} from '/@/tools/mina-zkapp-bridge';
+import isElectron from 'is-electron';
export default function NetworkSettings({
currentNetwork,
@@ -79,7 +80,7 @@ export default function NetworkSettings({
>
Settings
-
Current version: 2.1.1
+
Current version: 2.1.2
@@ -133,7 +134,7 @@ export default function NetworkSettings({
- {lockSession && (
+ {lockSession && isElectron() && (
{
const networksFound = availableNetworks.filter(network => network.chainId === chainId);
if (networksFound.length === 0) {
@@ -358,7 +357,6 @@ export default function ZkappIntegration() {
}));
};
- // TODO: Implement stake delegation
const stakeDelegation = async (data: any) => {
console.log('Received stake-delegation');
if (!config.isAuthenticated) {
diff --git a/packages/renderer/src/components/ledgerLogin/LedgerGetAddress.tsx b/packages/renderer/src/components/ledgerLogin/LedgerGetAddress.tsx
index 72ade21d..d1863999 100644
--- a/packages/renderer/src/components/ledgerLogin/LedgerGetAddress.tsx
+++ b/packages/renderer/src/components/ledgerLogin/LedgerGetAddress.tsx
@@ -8,10 +8,10 @@ import LedgerConfirmAddress from './LedgerConfirmAddress';
import type {IWalletIdData} from '../../types/WalletIdData';
import LedgerLoader from '../UI/ledgerLogin/LedgerLoader';
import {ArrowLeft} from 'react-feather';
-import {getPublicKey} from '/@/tools/ledger/ledgerElectronAPI';
import {useNavigate} from 'react-router-dom';
import {useSetRecoilState} from 'recoil';
import {configState, walletState} from '/@/store';
+import { getPublicKey } from '/@/tools/ledger/ledger';
interface IProps {
accountNumber?: number;
diff --git a/packages/renderer/src/components/transactionsTable/TransactionIcon.tsx b/packages/renderer/src/components/transactionsTable/TransactionIcon.tsx
index 7e6f030e..195f5331 100644
--- a/packages/renderer/src/components/transactionsTable/TransactionIcon.tsx
+++ b/packages/renderer/src/components/transactionsTable/TransactionIcon.tsx
@@ -1,10 +1,4 @@
-import {
- Check,
- ChevronRight,
- ChevronsDown,
- ChevronsUp,
- AlertTriangle,
-} from 'react-feather';
+import {Check, ChevronRight, ChevronsDown, ChevronsUp, AlertTriangle} from 'react-feather';
const TransactionIcon = (
txType: string,
@@ -12,9 +6,20 @@ const TransactionIcon = (
receiver: string,
userAddress: string,
isScam: boolean,
+ failed: boolean,
+ reason?: string,
) => {
if (isScam) {
- return
;
+ return (
+
+ );
+ }
+ // TODO: Change icon
+ if (failed) {
+ return
;
}
if (txType === 'delegation') {
return
;
@@ -22,9 +27,20 @@ const TransactionIcon = (
if (receiver === sender) {
return
;
} else if (userAddress === sender) {
- return
;
+ return (
+
+ );
} else if (userAddress === receiver) {
- return
;
+ return (
+
+ );
} else {
return
;
}
diff --git a/packages/renderer/src/components/transactionsTable/TransactionRow.tsx b/packages/renderer/src/components/transactionsTable/TransactionRow.tsx
index c3676bc5..20701d32 100644
--- a/packages/renderer/src/components/transactionsTable/TransactionRow.tsx
+++ b/packages/renderer/src/components/transactionsTable/TransactionRow.tsx
@@ -4,9 +4,21 @@ import type {BlacklistedAddress} from '../../types/Blacklist';
import TransactionIcon from './TransactionIcon';
import type {ITransactionRowData} from './TransactionsTypes';
import {useNetworkSettingsContext} from '/@/contexts/NetworkContext';
+import {formatUrl} from './TransactionsHelper';
const TransactionRow = (
- {timestamp, amount, sender, receiver, fee, memo, id, type}: ITransactionRowData,
+ {
+ timestamp,
+ amount,
+ sender,
+ receiver,
+ fee,
+ memo,
+ id,
+ type,
+ failed,
+ failure_reason,
+ }: ITransactionRowData,
index: number,
userAddress: string,
blacklist: BlacklistedAddress[],
@@ -53,14 +65,22 @@ const TransactionRow = (
>
{' '}
- {TransactionIcon(type, sender, receiver, userAddress, isScam)}{' '}
+ {TransactionIcon(
+ type,
+ sender,
+ receiver,
+ userAddress,
+ isScam,
+ failed,
+ failure_reason,
+ )}{' '}
|
openLinkOnBrowser(`${settings?.explorerUrl}${urlPath}/${id}`)}
+ onClick={() => !isMempool && openLinkOnBrowser(formatUrl(id, settings?.explorerUrl))}
target="_blank"
rel="noreferrer"
className="purple-text"
diff --git a/packages/renderer/src/components/transactionsTable/TransactionsHelper.ts b/packages/renderer/src/components/transactionsTable/TransactionsHelper.ts
index 0b23755e..d5686c61 100644
--- a/packages/renderer/src/components/transactionsTable/TransactionsHelper.ts
+++ b/packages/renderer/src/components/transactionsTable/TransactionsHelper.ts
@@ -14,7 +14,7 @@ export const mempoolQueryRowToTableRow = (mempoolRow: IMempoolQueryData) => {
const isSelf = receiver === sender;
const fee = 'Fee : ' + (mempoolRow.fee ? +toMINA(+mempoolRow.fee) : 0) + ' Mina';
const memo = mempoolRow.memo;
- const type = '';
+ const type = mempoolRow.kind === 'STAKE_DELEGATION' ? 'delegation' : 'payment';
return {
id,
amount,
@@ -33,15 +33,16 @@ export const mempoolQueryRowToTableRow = (mempoolRow: IMempoolQueryData) => {
* @returns ITransactionRowData
*/
export const transactionQueryRowToTableRow = (transactionRow: ITransactionQueryData) => {
- const {timestamp} = transactionRow;
+ const {timestamp, status, failure_reason} = transactionRow;
const id = transactionRow.hash;
const amount = transactionRow.amount ? toMINA(transactionRow.amount) : 0;
const sender = transactionRow.sender_public_key;
const receiver = transactionRow.receiver_public_key;
const fee = 'Fee : ' + (transactionRow.fee ? +toMINA(transactionRow.fee) : 0) + ' Mina';
- const type = transactionRow.type;
+ const type = transactionRow.command_type;
const isSelf = receiver === sender;
const memo = transactionRow.memo;
+ const failed = status === 'failed';
return {
id,
amount,
@@ -52,5 +53,16 @@ export const transactionQueryRowToTableRow = (transactionRow: ITransactionQueryD
memo,
timestamp,
type,
+ status,
+ failure_reason,
+ failed,
};
};
+
+export const formatUrl = (txId: string,url?: string) => {
+ if (url.includes('minascan.io')) {
+ return `${url}tx/${txId}`;
+ } else {
+ return `${url}transaction/${txId}`;
+ }
+};
diff --git a/packages/renderer/src/components/transactionsTable/TransactionsTypes.ts b/packages/renderer/src/components/transactionsTable/TransactionsTypes.ts
index cdc03820..82fe1545 100644
--- a/packages/renderer/src/components/transactionsTable/TransactionsTypes.ts
+++ b/packages/renderer/src/components/transactionsTable/TransactionsTypes.ts
@@ -7,6 +7,9 @@ export interface ITransactionQueryData {
amount: string;
hash: string;
timestamp: string;
+ status: string;
+ command_type: string;
+ failure_reason: string;
}
export interface IMempoolQueryData {
@@ -20,6 +23,7 @@ export interface IMempoolQueryData {
fee?: number;
memo: string;
id: string;
+ kind: string;
}
export interface ITransactionTableProps {
@@ -45,6 +49,9 @@ export interface ITransactionRowData {
memo: string;
type: string;
timestamp?: number;
+ status: string;
+ failure_reason?: string;
+ failed: boolean;
}
export interface ITransactionQueryResult {
diff --git a/packages/renderer/src/components/transactionsTable/WalletCreationTransaction.tsx b/packages/renderer/src/components/transactionsTable/WalletCreationTransaction.tsx
index 1e0a8d9e..ef829d49 100644
--- a/packages/renderer/src/components/transactionsTable/WalletCreationTransaction.tsx
+++ b/packages/renderer/src/components/transactionsTable/WalletCreationTransaction.tsx
@@ -1,16 +1,14 @@
-import { ChevronsUp } from 'react-feather';
+import {ChevronsUp} from 'react-feather';
const WalletCreationTransaction = (index: number) => {
return (
-
- {' '}
-
- |
-
+ |
1 Mina wallet creation fee
|
- {/* 1 Mina | */}
);
};
diff --git a/packages/renderer/src/pages/Login.tsx b/packages/renderer/src/pages/Login.tsx
index 16dcbdda..2df14cf3 100644
--- a/packages/renderer/src/pages/Login.tsx
+++ b/packages/renderer/src/pages/Login.tsx
@@ -1,6 +1,6 @@
import {Link, useNavigate} from 'react-router-dom';
import {useState, useEffect, useCallback} from 'react';
-import {useQuery} from '@apollo/client';
+import {useLazyQuery, useQuery} from '@apollo/client';
import {toast} from 'react-toastify';
import {ArrowLeft, ArrowRight} from 'react-feather';
import {deriveAccount, setPassphrase, spellMnemonic, storeAccounts, storeSession} from '/@/tools';
@@ -26,15 +26,10 @@ function Login({toggleLoader}: IProps) {
const [showPasswordModal, setShowPasswordModal] = useState(false);
const [passphraseError, setPassphraseError] = useState(null);
const navigate = useNavigate();
- const {
- data: userIdData,
- error: userIdError,
- loading: userIdLoading,
- refetch: userIdRefetch,
- } = useQuery(GET_ID, {
- variables: {publicKey},
- skip: !publicKey,
- });
+ const [userIdFetch, {data: userIdData, error: userIdError, loading: userIdLoading}] =
+ useLazyQuery(GET_ID, {
+ variables: {publicKey},
+ });
const setConfig = useSetRecoilState(configState);
const {encryptData} = useSecureStorage();
@@ -62,11 +57,15 @@ function Login({toggleLoader}: IProps) {
};
}, []);
- const saveAndStoreSession = () => {
+ const saveAndStoreSession = (userId?: string | number, derivedPublicKey?: string) => {
if (publicKey && publicKey !== '' && !userIdLoading && userIdData) {
toggleLoader(true);
const id = +userIdData?.idByPublicKey.id || -1;
- storeSessionAndRedirect(publicKey, id);
+ storeSessionAndRedirect(derivedPublicKey || publicKey, id);
+ } else if (derivedPublicKey) {
+ toggleLoader(true);
+ const id = +userId || -1;
+ storeSessionAndRedirect(derivedPublicKey || publicKey, id);
}
};
@@ -85,7 +84,7 @@ function Login({toggleLoader}: IProps) {
}, [userIdError]);
const storeSessionAndRedirect = async (publicKey: string, id: number) => {
- await userIdRefetch({publicKey});
+ await userIdFetch({variables: {publicKey}});
const isUsingMnemonic = privateKey.trim().split(' ').length === 12;
if (storePassphrase) {
setPassphrase(isUsingMnemonic);
@@ -161,11 +160,11 @@ function Login({toggleLoader}: IProps) {
const derivedAccount = await deriveAccount(privateKey.trim());
if (derivedAccount.publicKey) {
setPublicKey(derivedAccount.publicKey);
- await userIdRefetch({publicKey: derivedAccount.publicKey});
+ const {data} = await userIdFetch({variables: {publicKey: derivedAccount.publicKey}});
if (storePassphrase) {
setShowPasswordModal(true);
} else {
- saveAndStoreSession();
+ saveAndStoreSession(data?.idByPublicKey?.id, derivedAccount.publicKey);
}
}
} catch (e) {
diff --git a/packages/renderer/src/pages/Overview.tsx b/packages/renderer/src/pages/Overview.tsx
index 8a97b0fd..e328f99c 100644
--- a/packages/renderer/src/pages/Overview.tsx
+++ b/packages/renderer/src/pages/Overview.tsx
@@ -131,11 +131,17 @@ const Overview = ({sessionData}: IProps) => {
};
useEffect(() => {
- if (transactionsData && mempoolData) {
+ if (transactionsData) {
setLoading(false);
}
}, [transactionsData, mempoolData]);
+ useEffect(() => {
+ if (transactionsError) {
+ setLoading(false);
+ }
+ }, [transactionsError]);
+
return (
diff --git a/packages/renderer/src/pages/ZkApps.tsx b/packages/renderer/src/pages/ZkApps.tsx
index 35d570ed..65c67fbb 100644
--- a/packages/renderer/src/pages/ZkApps.tsx
+++ b/packages/renderer/src/pages/ZkApps.tsx
@@ -1,11 +1,13 @@
import {ZkappSidebar} from '../components/UI/modals/zkAppIntegration/ZkappSidebar';
import {ZkappIframe} from '../components/UI/modals/zkAppIntegration/ZkappIframe';
+import { ZkappConnectedApps } from '../components/UI/modals/zkAppIntegration/ZkappConnectedApps';
const ZkApps = () => {
return (
-
-
+ {/*
+ */}
+
);
};
diff --git a/packages/renderer/src/pages/sendTX/SendTX.tsx b/packages/renderer/src/pages/sendTX/SendTX.tsx
index 2b59e44f..df75fde7 100644
--- a/packages/renderer/src/pages/sendTX/SendTX.tsx
+++ b/packages/renderer/src/pages/sendTX/SendTX.tsx
@@ -68,12 +68,12 @@ function SendTX(props: IProps) {
const [transactionData, setTransactionData] = useState (initialTransactionData);
const [ledgerTransactionData, setLedgerTransactionData] = useState('');
const [storedPassphrase, setStoredPassphrase] = useState('');
- const {isLedgerEnabled} = useContext>(LedgerContext);
const {getBalance, setShouldBalanceUpdate} = useContext>(BalanceContext);
// const {wallet} = useWallet();
const wallet = useRecoilValue(walletState);
const senderAddress = wallet.address;
const balance = getBalance && getBalance(wallet.address);
+ const isLedgerEnabled = wallet.ledger;
const {
data: nonceData,
refetch: nonceRefetch,
diff --git a/packages/renderer/src/pages/stake/Stake.tsx b/packages/renderer/src/pages/stake/Stake.tsx
index 5bb16cdd..4e2d7885 100644
--- a/packages/renderer/src/pages/stake/Stake.tsx
+++ b/packages/renderer/src/pages/stake/Stake.tsx
@@ -70,9 +70,9 @@ const Stake = ({sessionData}: IProps) => {
const [selectedFee, setSelectedFee] = useState(0);
const [ledgerTransactionData, setLedgerTransactionData] = useState('');
const [sendTransactionFlag, setSendTransactionFlag] = useState(false);
- const {isLedgerEnabled} = useContext>(LedgerContext);
+ // const {isLedgerEnabled} = useContext>(LedgerContext);
const {getBalance, setShouldBalanceUpdate} = useContext>(BalanceContext);
- const {address, accountNumber} = useRecoilValue(walletState);
+ const {address, accountNumber,ledger:isLedgerEnabled} = useRecoilValue(walletState);
const balance = getBalance && getBalance(address);
const {
diff --git a/packages/renderer/src/styles/__misc.scss b/packages/renderer/src/styles/__misc.scss
index d0f64320..a1084c14 100644
--- a/packages/renderer/src/styles/__misc.scss
+++ b/packages/renderer/src/styles/__misc.scss
@@ -900,11 +900,12 @@ tbody {
#draggable-bar {
width: 100vw;
- min-height: 2vh;
- position: none;
+ min-height: 4vh;
+ position: absolute;
left: 0px;
top: 0;
-webkit-app-region: drag;
+ z-index: 9999;
}
.huge-text {
diff --git a/packages/renderer/src/styles/__zkapps.scss b/packages/renderer/src/styles/__zkapps.scss
new file mode 100644
index 00000000..bdc15be3
--- /dev/null
+++ b/packages/renderer/src/styles/__zkapps.scss
@@ -0,0 +1,43 @@
+.zkapps-list-container {
+ gap: 40px;
+ margin-top: 50px;
+ flex-wrap: wrap;
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
+}
+
+.zkapp-favicon {
+ width: 30px;
+ position: relative;
+ top: -20px;
+}
+
+.zkapp-input {
+ max-width: 1200px;
+ margin-top: 20px;
+ display: flex;
+ flex-direction: row;
+ gap: 20px;
+}
+
+.zkapp-open-button {
+ height: 45px;
+ max-width: 250px;
+}
+
+.zkapp-warning-alert {
+ display: flex;
+ justify-content: start;
+ align-items: center;
+ gap: 20px;
+}
+
+.zkapp-list-item {
+ transition: all 0.3s;
+ min-width: 350px;
+ margin: 0 auto;
+}
+
+.zkapp-list-item:hover {
+ margin-top: -5px;
+}
|