add unlock transaction

This commit is contained in:
kandrusyak
2022-12-09 11:53:36 +03:00
parent 68e665804c
commit 5939bb4e13
6 changed files with 80 additions and 137 deletions

View File

@@ -6,6 +6,7 @@
"endOfLine": "lf", "endOfLine": "lf",
"jsxBracketSameLine": false, "jsxBracketSameLine": false,
"trailingComma": "es5", "trailingComma": "es5",
"printWidth": 100,
"overrides": [ "overrides": [
{ {
"files": "*.json", "files": "*.json",

View File

@@ -81,7 +81,11 @@ const Delegates = observer(() => {
<TransactionsTable <TransactionsTable
data={delegates} data={delegates}
lockedFor={lockedFor} lockedFor={lockedFor}
rowClick={(delegate) => setCurrentDelegate(delegate)} rowClick={(delegate) =>
(lockedFor === 'Voiting' ||
store.accountLockedVotesCanReturnSum[delegate.address]) &&
setCurrentDelegate(delegate)
}
/> />
{/*/!* Pagination *!/*/} {/*/!* Pagination *!/*/}
@@ -99,9 +103,11 @@ const Delegates = observer(() => {
transactionPanelOpen={!!currentDelegate} transactionPanelOpen={!!currentDelegate}
onClose={() => setCurrentDelegate(null)} onClose={() => setCurrentDelegate(null)}
delegate={currentDelegate || {}} delegate={currentDelegate || {}}
postTransaction={(address, amount) => unlocking={lockedFor !== 'Voiting'}
store.pushVoteTransaction(address, amount) postTransaction={(address, amount) => {
} if (lockedFor === 'Voiting') store.pushVoteTransaction(address, amount);
else store.pushUnlockTransaction(address);
}}
/> />
</div> </div>
</main> </main>

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import Image from '../../images/transactions-image-04.svg'; import Image from '../../images/transactions-image-04.svg';
import moment from 'moment'; import moment from 'moment';
@@ -9,16 +9,19 @@ import UserAvatar from '../../images/user-avatar-32.png';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
const TransactionPanel = observer( const TransactionPanel = observer(
({ transactionPanelOpen, onClose, delegate, postTransaction }) => { ({ transactionPanelOpen, onClose, delegate, postTransaction, unlocking }) => {
const closeBtn = useRef(null); const closeBtn = useRef(null);
const panelContent = useRef(null); const panelContent = useRef(null);
const [amount, setAmount] = useState(0); const [amount, setAmount] = useState(0);
const status = useMemo(() => (unlocking ? 'Unlock' : delegate.status), [unlocking, delegate]);
const statusColor = () => { const statusColor = () => {
switch (delegate.status) { switch (status) {
case 'Vote': case 'Vote':
return 'bg-emerald-100 text-emerald-600'; return 'bg-emerald-100 text-emerald-600';
case 'Unlock':
case 'Unvote': case 'Unvote':
return 'bg-rose-100 text-rose-500'; return 'bg-rose-100 text-rose-500';
default: default:
@@ -26,8 +29,7 @@ const TransactionPanel = observer(
} }
}; };
const amountLabel = const amountLabel = delegate.status === 'Vote' ? 'Voiting amount' : 'Unvoiting amount';
delegate.status === 'Vote' ? 'Voiting amount' : 'Unvoiting amount';
// close if the esc key is pressed // close if the esc key is pressed
useEffect(() => { useEffect(() => {
@@ -40,12 +42,13 @@ const TransactionPanel = observer(
}); });
useEffect(() => { useEffect(() => {
const status = delegate.status;
if (status === 'Unvote') { if (status === 'Unvote') {
setAmount(store.accountSentVotes[delegate.address].toString()); setAmount(store.accountSentVotes[delegate.address].toString());
} }
}, [delegate]); if (status === 'Unlock') {
setAmount(store.accountLockedVotesCanReturnSum[delegate.address]?.toString());
}
}, [delegate, status]);
return ( return (
<div <div
@@ -70,9 +73,7 @@ const TransactionPanel = observer(
</button> </button>
<div className="py-8 px-4 lg:px-8"> <div className="py-8 px-4 lg:px-8">
<div className="max-w-sm mx-auto lg:max-w-none"> <div className="max-w-sm mx-auto lg:max-w-none">
<div className="text-slate-800 font-semibold text-center mb-1"> <div className="text-slate-800 font-semibold text-center mb-1">Vote Transaction</div>
Vote Transaction
</div>
<div className="text-sm text-center italic"> <div className="text-sm text-center italic">
{moment().format('DD/MM/YYYY hh:mm A')} {moment().format('DD/MM/YYYY hh:mm A')}
</div> </div>
@@ -89,40 +90,28 @@ const TransactionPanel = observer(
alt="Transaction 04" alt="Transaction 04"
/> />
</div> </div>
<div <div className={`text-2xl font-semibold mb-1 ${statusColor()} bg-transparent`}>
className={`text-2xl font-semibold mb-1 ${statusColor()} bg-transparent`}
>
{amount}/idn {amount}/idn
</div> </div>
<div className="text-sm font-medium text-slate-800 mb-3"> <div className="text-sm font-medium text-slate-800 mb-3">
{delegate?.dpos?.delegate?.username || {delegate?.dpos?.delegate?.username || formatAddressBig(delegate.address || '')}
formatAddressBig(delegate.address || '')}
</div> </div>
<div <div
className={`text-xs inline-flex font-medium rounded-full text-center px-2.5 py-1 ${statusColor()} justify-center`} className={`text-xs inline-flex font-medium rounded-full text-center px-2.5 py-1 ${statusColor()} justify-center`}
style={{ minWidth: '84px' }} style={{ minWidth: '84px' }}
> >
{delegate.status} {status}
</div> </div>
</div> </div>
{/* Divider */} {/* Divider */}
<div <div className="flex justify-between items-center" aria-hidden="true">
className="flex justify-between items-center" <svg className="w-5 h-5 fill-white" xmlns="http://www.w3.org/2000/svg">
aria-hidden="true"
>
<svg
className="w-5 h-5 fill-white"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M0 20c5.523 0 10-4.477 10-10S5.523 0 0 0h20v20H0Z" /> <path d="M0 20c5.523 0 10-4.477 10-10S5.523 0 0 0h20v20H0Z" />
</svg> </svg>
<div className="grow w-full h-5 bg-white flex flex-col justify-center"> <div className="grow w-full h-5 bg-white flex flex-col justify-center">
<div className="h-px w-full border-t border-dashed border-slate-200" /> <div className="h-px w-full border-t border-dashed border-slate-200" />
</div> </div>
<svg <svg className="w-5 h-5 fill-white rotate-180" xmlns="http://www.w3.org/2000/svg">
className="w-5 h-5 fill-white rotate-180"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M0 20c5.523 0 10-4.477 10-10S5.523 0 0 0h20v20H0Z" /> <path d="M0 20c5.523 0 10-4.477 10-10S5.523 0 0 0h20v20H0Z" />
</svg> </svg>
</div> </div>
@@ -136,9 +125,7 @@ const TransactionPanel = observer(
</div> </div>
<div className="flex justify-between space-x-1"> <div className="flex justify-between space-x-1">
<span className="italic">FEE:</span> <span className="italic">FEE:</span>
<span className="font-medium text-slate-700 text-right"> <span className="font-medium text-slate-700 text-right">145/idn</span>
145/idn
</span>
</div> </div>
<div className="flex justify-between space-x-1"> <div className="flex justify-between space-x-1">
<span className="italic">Nonce:</span> <span className="italic">Nonce:</span>
@@ -156,11 +143,9 @@ const TransactionPanel = observer(
</div> </div>
{/* Receipts */} {/* Receipts */}
<div className="mt-6"> <div className="mt-6">
<label <label className="block text-sm font-medium mb-1" htmlFor="mandatory">
className="block text-sm font-medium mb-1" {unlocking ? 'Unlock amount' : amountLabel}{' '}
htmlFor="mandatory" <span className="text-rose-500">*</span>
>
{amountLabel} <span className="text-rose-500">*</span>
</label> </label>
<input <input
id="amount" id="amount"
@@ -169,13 +154,12 @@ const TransactionPanel = observer(
required required
onChange={(e) => setAmount(e.target.value)} onChange={(e) => setAmount(e.target.value)}
value={amount} value={amount}
disabled={unlocking}
/> />
</div> </div>
{/* Notes */} {/* Notes */}
<div className="mt-6"> <div className="mt-6">
<div className="text-sm font-semibold text-slate-800 mb-2"> <div className="text-sm font-semibold text-slate-800 mb-2">Transaction details</div>
Transaction details
</div>
<div className="flex p-2 justify-between items-center border border-slate-300 rounded-md"> <div className="flex p-2 justify-between items-center border border-slate-300 rounded-md">
<div className="flex gap-2"> <div className="flex gap-2">
<img <img
@@ -185,9 +169,7 @@ const TransactionPanel = observer(
height="32" height="32"
alt="User" alt="User"
/> />
<div className="flex items-center text-sm text-slate-700"> <div className="flex items-center text-sm text-slate-700">Balance</div>
Balance
</div>
</div> </div>
<div className="flex items-center text-sm text-slate-700"> <div className="flex items-center text-sm text-slate-700">
{store.accountBalance}/idn {store.accountBalance}/idn

View File

@@ -47,22 +47,16 @@ const TransactionsTable = observer(({ data, rowClick, lockedFor }) => {
balance={delegate?.token?.balance} balance={delegate?.token?.balance}
name={delegate.dpos?.delegate?.username || delegate.address} name={delegate.dpos?.delegate?.username || delegate.address}
total={delegate.dpos?.delegate?.totalVotesReceived || ''} total={delegate.dpos?.delegate?.totalVotesReceived || ''}
hasSevice={ hasSevice={delegate.dpos?.delegate?.username === 'delegate_0'}
delegate.dpos?.delegate?.username === 'delegate_0'
}
date={delegate.date} date={delegate.date}
displayReturn={lockedFor === 'Unlocking'} displayReturn={lockedFor === 'Unlocking'}
status={delegate.status} status={delegate.status}
amount={ amount={
(lockedFor === 'Voiting' (lockedFor === 'Voiting'
? store.accountSentVotes[delegate.address] ? store.accountSentVotes[delegate.address]
: store.accountLockedVotesCanReturnSum[ : store.accountLockedVotesCanReturnSum[delegate.address]) || ''
delegate.address
]) || ''
}
handleClick={() =>
lockedFor === 'Voiting' && rowClick(delegate)
} }
handleClick={() => rowClick(delegate)}
/> />
); );
})} })}

View File

@@ -24,11 +24,6 @@ const TransactionsTableItem = observer((props) => {
} }
}; };
const clickReturn = (e) => {
e.stopPropagation();
store.pushUnlockTransaction(props.id);
};
return ( return (
<tr className="cursor-pointer" onClick={() => props.handleClick()}> <tr className="cursor-pointer" onClick={() => props.handleClick()}>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap md:w-1/2"> <td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap md:w-1/2">
@@ -71,14 +66,14 @@ const TransactionsTableItem = observer((props) => {
</td> </td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap"> <td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="text-center"> <div className="text-center">
{props.displayReturn ? ( {props.displayReturn && props.status !== 'Pending' ? (
store.accountLockedVotesCanReturn[props.id] && ( store.accountLockedVotesCanReturn[props.id] && (
<span <div
className="truncate font-medium text-indigo-500 group-hover:text-indigo-600 ml-2" className={`text-xs inline-flex font-medium rounded-full justify-center px-2.5 py-1 bg-rose-100 text-rose-500`}
onClick={clickReturn} style={{ minWidth: '84px' }}
> >
Return votes Unlock
</span> </div>
) )
) : ( ) : (
<div <div

View File

@@ -1,9 +1,4 @@
import { import { reaction, makeAutoObservable, onBecomeObserved, onBecomeUnobserved } from 'mobx';
reaction,
makeAutoObservable,
onBecomeObserved,
onBecomeUnobserved,
} from 'mobx';
import { cryptography, transactions } from '@liskhq/lisk-client'; import { cryptography, transactions } from '@liskhq/lisk-client';
import { passphrase } from '@liskhq/lisk-client'; import { passphrase } from '@liskhq/lisk-client';
import { getNodeInfo } from '../api/node'; import { getNodeInfo } from '../api/node';
@@ -67,20 +62,12 @@ class Store {
() => this.address, () => this.address,
() => this.fetchAccountInfo() () => this.fetchAccountInfo()
); );
onBecomeObserved(this, 'decryptedAccountData', () => onBecomeObserved(this, 'decryptedAccountData', () => this.fetchNewAccountData());
this.fetchNewAccountData() onBecomeUnobserved(this, 'decryptedAccountData', () => this.unobservedAccountData());
);
onBecomeUnobserved(this, 'decryptedAccountData', () =>
this.unobservedAccountData()
);
onBecomeObserved(this, 'sharedData', () => this.fetchKeysArray()); onBecomeObserved(this, 'sharedData', () => this.fetchKeysArray());
onBecomeUnobserved(this, 'sharedData', () => this.unobservedSharedData()); onBecomeUnobserved(this, 'sharedData', () => this.unobservedSharedData());
onBecomeObserved(this, 'transactionsInfo', () => onBecomeObserved(this, 'transactionsInfo', () => this.fetchTransactionsInfo());
this.fetchTransactionsInfo() onBecomeUnobserved(this, 'transactionsInfo', () => this.unobservedTransactionsInfo());
);
onBecomeUnobserved(this, 'transactionsInfo', () =>
this.unobservedTransactionsInfo()
);
this.fetchNodeInfo(); this.fetchNodeInfo();
} }
@@ -342,8 +329,7 @@ class Store {
fetchAccountInfoSuccess(res) { fetchAccountInfoSuccess(res) {
this._accountInfo = res.data; this._accountInfo = res.data;
if (this.accountInfo?.sequence?.nonce) if (this.accountInfo?.sequence?.nonce)
this.accountInfo.sequence.nonce = this.accountInfo.sequence.nonce = parseInt(this.accountInfo.sequence.nonce) || 0;
parseInt(this.accountInfo.sequence.nonce) || 0;
} }
fetchTempTransactionsSuccess(res) { fetchTempTransactionsSuccess(res) {
@@ -370,24 +356,23 @@ class Store {
.map((item) => { .map((item) => {
return { return {
id: item.id, id: item.id,
sender_avatar: sender_avatar: item?.asset?.recipientAddress && generateSvgAvatar(item.senderPublicKey),
item.asset.recipientAddress &&
generateSvgAvatar(item.senderPublicKey),
avatar_size: 24, avatar_size: 24,
transaction: item.asset.features.map((asset) => { transaction:
return { item.asset?.features?.map((asset) => {
transaction_id: item.id, return {
address: transaction_id: item.id,
item.asset.recipientAddress && address:
cryptography.bufferToHex( item.asset.recipientAddress &&
cryptography.getAddressFromPublicKey( cryptography.bufferToHex(
cryptography.hexToBuffer(item.senderPublicKey) cryptography.getAddressFromPublicKey(
) cryptography.hexToBuffer(item.senderPublicKey)
), )
value: asset.value, ),
label: labelMap[asset.label] || asset.label, value: asset.value,
}; label: labelMap[asset.label] || asset.label,
}), };
}) || [],
}; };
}); });
} }
@@ -402,13 +387,11 @@ class Store {
} }
get firstName() { get firstName() {
return this.decryptedAccountData.find((item) => item.key === 'firstname') return this.decryptedAccountData.find((item) => item.key === 'firstname')?.value;
?.value;
} }
get lastName() { get lastName() {
return this.decryptedAccountData.find((item) => item.key === 'secondname') return this.decryptedAccountData.find((item) => item.key === 'secondname')?.value;
?.value;
} }
get accountName() { get accountName() {
@@ -440,16 +423,12 @@ class Store {
} }
get privateKey() { get privateKey() {
return cryptography.getPrivateAndPublicKeyFromPassphrase(this.passPhrase) return cryptography.getPrivateAndPublicKeyFromPassphrase(this.passPhrase)?.privateKey;
?.privateKey;
} }
get vpnPrivateKey() { get vpnPrivateKey() {
let x25519_sk = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES); let x25519_sk = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES);
sodium.crypto_sign_ed25519_sk_to_curve25519( sodium.crypto_sign_ed25519_sk_to_curve25519(x25519_sk, Buffer.from(this.privateKey, 'hex'));
x25519_sk,
Buffer.from(this.privateKey, 'hex')
);
return x25519_sk.toString('Base64'); return x25519_sk.toString('Base64');
} }
@@ -462,8 +441,7 @@ class Store {
} }
get tokenKey() { get tokenKey() {
const stringToSign = const stringToSign = this.nodeInfo.networkIdentifier + this.nodeInfo.lastBlockID;
this.nodeInfo.networkIdentifier + this.nodeInfo.lastBlockID;
if (!stringToSign) return ''; if (!stringToSign) return '';
const sign = cryptography const sign = cryptography
.signDataWithPassphrase(Buffer.from(stringToSign, 'hex'), this.passPhrase) .signDataWithPassphrase(Buffer.from(stringToSign, 'hex'), this.passPhrase)
@@ -480,17 +458,14 @@ class Store {
} }
get accountBalance() { get accountBalance() {
return ( return (BigInt(this.accountInfo?.token?.balance || 0) / 100000000n).toString();
BigInt(this.accountInfo?.token?.balance || 0) / 100000000n
).toString();
} }
get accountSentVotes() { get accountSentVotes() {
return (this.accountInfo?.dpos?.sentVotes || []).reduce( return (this.accountInfo?.dpos?.sentVotes || []).reduce(
(acc, e) => ({ (acc, e) => ({
...acc, ...acc,
[e.delegateAddress]: [e.delegateAddress]: BigInt(e.amount || 0) / 100000000n + (acc[e.delegateAddress] || 0n),
BigInt(e.amount || 0) / 100000000n + (acc[e.delegateAddress] || 0n),
}), }),
{} {}
); );
@@ -500,8 +475,7 @@ class Store {
return (this.accountInfo?.dpos?.unlocking || []).reduce( return (this.accountInfo?.dpos?.unlocking || []).reduce(
(acc, e) => ({ (acc, e) => ({
...acc, ...acc,
[e.delegateAddress]: [e.delegateAddress]: BigInt(e.amount || 0) / 100000000n + (acc[e.delegateAddress] || 0n),
BigInt(e.amount || 0) / 100000000n + (acc[e.delegateAddress] || 0n),
}), }),
{} {}
); );
@@ -563,10 +537,7 @@ class Store {
const { features } = item.asset; const { features } = item.asset;
return { return {
...acc, ...acc,
...(features?.reduce( ...(features?.reduce((obj, feature) => ({ ...obj, [feature.label]: true }), {}) || {}),
(obj, feature) => ({ ...obj, [feature.label]: true }),
{}
) || {}),
}; };
}, {}); }, {});
} }
@@ -582,10 +553,7 @@ class Store {
(obj, vote) => ({ (obj, vote) => ({
...obj, ...obj,
[vote.delegateAddress]: [vote.delegateAddress]:
BigInt( BigInt(Number(vote.amount) > 0 ? vote.amount : vote.amount.slice(1)) / 100000000n +
Number(vote.amount) > 0 ? vote.amount : vote.amount.slice(1)
) /
100000000n +
(obj[vote.delegateAddress] || 0n), (obj[vote.delegateAddress] || 0n),
}), }),
{} {}
@@ -605,8 +573,7 @@ class Store {
(obj, vote) => ({ (obj, vote) => ({
...obj, ...obj,
[vote.delegateAddress]: [vote.delegateAddress]:
BigInt(vote.amount) / 100000000n + BigInt(vote.amount) / 100000000n + (obj[vote.delegateAddress] || 0n),
(obj[vote.delegateAddress] || 0n),
}), }),
{} {}
) || {}), ) || {}),
@@ -617,9 +584,7 @@ class Store {
get vpnServers() { get vpnServers() {
return this._vpnServers?.map((server) => ({ return this._vpnServers?.map((server) => ({
...server, ...server,
transferSum: formatBytes( transferSum: formatBytes(Number(server.transferTx || 0) + Number(server.transferRx || 0)),
Number(server.transferTx || 0) + Number(server.transferRx || 0)
),
trafficUsed: Math.floor( trafficUsed: Math.floor(
((Number(server.transferTx || 0) + Number(server.transferRx || 0)) / ((Number(server.transferTx || 0) + Number(server.transferRx || 0)) /
(50 * 1024 * 1024 * 1024)) * (50 * 1024 * 1024 * 1024)) *
@@ -638,7 +603,7 @@ class Store {
const getStatus = (delegate) => { const getStatus = (delegate) => {
const { address } = delegate; const { address } = delegate;
let amount = this.processedVotes[address]; let amount = this.processedVotes[address] || this.processedUnlocking[address];
if (amount) return 'Pending'; if (amount) return 'Pending';