-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
APL 2334: offline sign #426
base: develop
Are you sure you want to change the base?
Changes from 11 commits
a25540c
a62d8d3
75b4896
ebe3461
2871e10
2a298ff
98f1aaf
02c2e61
23a2a46
b10429d
a0ac0b9
d824e3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
3.0.9 | ||
3.0.10 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "apollo-web-ui", | ||
"description": "Apollo blockchain web UI", | ||
"version": "3.0.9", | ||
"version": "3.0.10", | ||
"dependencies": [ ] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,45 +8,70 @@ import { useSelector, useDispatch, shallowEqual } from 'react-redux'; | |
import { NotificationManager } from 'react-notifications'; | ||
import { setBodyModalParamsAction } from 'modules/modals'; | ||
import { | ||
getAccountSelector, | ||
getAccountRsSelector, | ||
getDecimalsSelector, | ||
getModalDataSelector, | ||
getPassPhraseSelector, | ||
getTickerSelector | ||
} from 'selectors'; | ||
import ModalBody from 'containers/components/modals/modal-body'; | ||
import {PrivateTransactionConfirm} from './PrivateTransactionConfirm/PrivateTransactionConfirm'; | ||
import SendApolloForm from './form'; | ||
import { sendMoneyOfflineTransaction, checkIsVaultWallet } from 'helpers/transactions'; | ||
import { setModalProcessingFalseAction, setModalProcessingTrueAction } from 'actions/modals'; | ||
|
||
export default function SendApollo({ closeModal, processForm }) { | ||
const [ isShowNotification, setIsShowNotification ] = useState(false); | ||
const dispatch = useDispatch(); | ||
|
||
const modalData = useSelector(getModalDataSelector, shallowEqual); | ||
const account = useSelector(getAccountSelector); | ||
const accountRS = useSelector(getAccountRsSelector); | ||
const passPhrase = useSelector(getPassPhraseSelector); | ||
const ticker = useSelector(getTickerSelector); | ||
const decimals = useSelector(getDecimalsSelector); | ||
|
||
const handleFormSubmit = useCallback(async values => { | ||
console.log("🚀 ~ file: index.jsx:35 ~ handleFormSubmit ~ values:", values) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. убрать наверное надо |
||
const { privateTransaction, ...data } = values; | ||
if (!values.secretPhrase || values.secretPhrase.length === 0) { | ||
NotificationManager.error('Secret Phrase is required.', 'Error', 5000); | ||
return; | ||
} | ||
|
||
if (values.doNotSign) { | ||
data.publicKey = await crypto.getPublicKeyAPL(account, true); | ||
delete data.secretPhrase; | ||
} | ||
|
||
if (values.phasingFinishHeight) { | ||
data.phased = true; | ||
} | ||
dispatch(setModalProcessingTrueAction()) | ||
|
||
if (values.alias) { | ||
data.recipient = values.alias; | ||
} | ||
|
||
processForm({ decimals, ...data }, 'sendMoney', 'Transaction has been submitted!', res => { | ||
const isVaultWallet = checkIsVaultWallet(data.secretPhrase, accountRS); | ||
|
||
if (isVaultWallet) { | ||
processForm({ decimals, ...data }, 'sendMoney', 'Transaction has been submitted!', res => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. чтоб не повторять дважды кусок когда после получения res, то по идее можно в переменную получить res тут |
||
if (res.broadcasted === false) { | ||
dispatch(setBodyModalParamsAction('RAW_TRANSACTION_DETAILS', { | ||
request: data, | ||
result: res, | ||
})); | ||
} else { | ||
closeModal(); | ||
} | ||
|
||
NotificationManager.success('Transaction has been submitted!', null, 5000); | ||
}); | ||
return; | ||
} | ||
|
||
try { | ||
const res = await sendMoneyOfflineTransaction(data, accountRS, passPhrase); | ||
|
||
dispatch(setModalProcessingFalseAction()); | ||
|
||
if (res && res.errorCode) { | ||
NotificationManager.error(res.errorDescription, 'Error', 5000); | ||
return; | ||
} | ||
|
||
if (res.broadcasted === false) { | ||
dispatch(setBodyModalParamsAction('RAW_TRANSACTION_DETAILS', { | ||
request: data, | ||
|
@@ -55,10 +80,13 @@ export default function SendApollo({ closeModal, processForm }) { | |
} else { | ||
closeModal(); | ||
} | ||
|
||
NotificationManager.success('Transaction has been submitted!', null, 5000); | ||
}); | ||
}, [account, closeModal, decimals, dispatch, processForm]); | ||
} catch (e) { | ||
dispatch(setModalProcessingFalseAction()); | ||
NotificationManager.error('Transaction error', 'Error', 5000); | ||
} | ||
}, [closeModal, decimals, dispatch, passPhrase, accountRS, processForm]); | ||
|
||
const handleShowNotification = (value) => () => { | ||
setIsShowNotification(value); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import { Crypto, Transaction } from 'apl-web-crypto'; | ||
import { ONE_APL } from 'constants/constants'; | ||
import LocalCrypto from '../crypto/crypto'; | ||
import converters from '../converters'; | ||
|
||
export const checkIsVaultWallet = (secretPhrase, accountRS) => { | ||
const publicKey = Crypto.getPublicKey(secretPhrase); | ||
const userFromPublicKey = Crypto.getAccountIdFromPublicKey(publicKey, true); | ||
return accountRS !== userFromPublicKey; | ||
} | ||
|
||
// check user secretPhase and compare account RS from publicKey and from request | ||
// return publicKey | ||
const checkAccountForOfflineSignAndPublicKey = (secretPhrase, accountRS, appPassPhraseFromStore) => { | ||
|
||
if (!secretPhrase) throw new Error('Secret phrase is empty'); | ||
|
||
if (!accountRS) throw new Error('Set sender account'); | ||
|
||
if(appPassPhraseFromStore && secretPhrase !== appPassPhraseFromStore) { | ||
throw new Error('Incorrect secret phrase') | ||
} | ||
|
||
const publicKey = Crypto.getPublicKey(secretPhrase); | ||
|
||
const userFromPublicKey = Crypto.getAccountIdFromPublicKey(publicKey, true); | ||
|
||
if (accountRS !== userFromPublicKey) { | ||
throw new Error('Incorrect secret phrase'); | ||
} | ||
return publicKey; | ||
} | ||
|
||
export const sendMoneyOfflineTransaction = async ( | ||
{ | ||
secretPhrase, | ||
amountATM, | ||
feeATM, | ||
deadline = 1440, | ||
recipient, | ||
add_message, | ||
encrypt_message, | ||
permanent_message, | ||
message, | ||
}, | ||
accountRS, | ||
appPassPhraseFromStore | ||
) => { | ||
const publicKey = checkAccountForOfflineSignAndPublicKey(secretPhrase, accountRS, appPassPhraseFromStore); | ||
|
||
const data = { | ||
publicKey, | ||
requestType: 'sendMoney', | ||
amountATM: amountATM * ONE_APL, | ||
feeATM: feeATM * ONE_APL, | ||
deadline, | ||
recipient, | ||
}; | ||
|
||
|
||
if (add_message && encrypt_message) { | ||
const privateKey = Crypto.getPrivateKey(secretPhrase); | ||
|
||
const encrypted = LocalCrypto.encryptDataAPL(converters.stringToByteArray(message), { | ||
privateKey, | ||
publicKey, | ||
}) | ||
|
||
data.encryptedMessageData = converters.byteArrayToHexString(encrypted.data); | ||
data.encryptedMessageNonce = converters.byteArrayToHexString(encrypted.nonce); | ||
|
||
// if (recipient === accountRS) { | ||
// data.messageToEncryptToSelfIsText = "true" | ||
// } else { | ||
data.messageToEncryptIsText = "true" | ||
// } | ||
} | ||
|
||
if (add_message) { | ||
data.message = message; | ||
} | ||
|
||
if (add_message && permanent_message) { | ||
data.permanent_message = permanent_message; | ||
} | ||
|
||
const unsignedTransactionData = await Transaction.sendNotSign(data); | ||
const sendData = {secretPhrase: secretPhrase }; | ||
const signedResponse = await Transaction.processOfflineSign(sendData, unsignedTransactionData); | ||
|
||
const dataTransaction = { | ||
requestType: 'broadcastTransaction', | ||
transactionBytes: signedResponse.transactionBytes, | ||
} | ||
return await Transaction.send(dataTransaction); | ||
} | ||
|
||
export const sendCurrencyTransferOffline = async ( | ||
{secretPhrase, feeATM,...initialData}, | ||
accountRS, | ||
appPassPhraseFromStore | ||
) => { | ||
const publicKey = checkAccountForOfflineSignAndPublicKey(secretPhrase, accountRS, appPassPhraseFromStore); | ||
|
||
const unsignedTransactionData = await Transaction.sendNotSign({ | ||
...initialData, | ||
publicKey, | ||
requestType: 'transferCurrency', | ||
deadline: 1440, | ||
feeATM: feeATM * ONE_APL, | ||
}); | ||
|
||
const sendData = {secretPhrase: secretPhrase }; | ||
const signedResponse = await Transaction.processOfflineSign(sendData, unsignedTransactionData); | ||
|
||
const dataTransaction = { | ||
requestType: 'broadcastTransaction', | ||
transactionBytes: signedResponse.transactionBytes, | ||
} | ||
return await Transaction.send(dataTransaction); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1