diff --git a/app/components/DappRequest/InvokeFunction/InvokeFunction.jsx b/app/components/DappRequest/InvokeFunction/InvokeFunction.jsx index 9916b92ce..28f273a89 100644 --- a/app/components/DappRequest/InvokeFunction/InvokeFunction.jsx +++ b/app/components/DappRequest/InvokeFunction/InvokeFunction.jsx @@ -24,7 +24,7 @@ import Tooltip from '../../Tooltip' import Info from '../../../assets/icons/info.svg' import { getNode, getRPCEndpoint } from '../../../actions/nodeStorageActions' -import Invocation from './Invocation' +import Invocation from '../components/Invocation' import InvokeResult from './InvokeResult' const electron = require('electron') diff --git a/app/components/DappRequest/SignTransaction/SignTransaction.jsx b/app/components/DappRequest/SignTransaction/SignTransaction.jsx new file mode 100644 index 000000000..9ed398ecb --- /dev/null +++ b/app/components/DappRequest/SignTransaction/SignTransaction.jsx @@ -0,0 +1,262 @@ +/* eslint-disable no-nested-ternary */ +// @flow +import React, { useCallback, useEffect, useMemo, useState } from 'react' +import classNames from 'classnames' +import { wallet as n3Wallet } from '@cityofzion/neon-js' + +import { + useWalletConnectWallet, + TSession, + TSessionRequest, +} from '@cityofzion/wallet-connect-sdk-wallet-react' +import { NeonInvoker } from '@cityofzion/neon-dappkit' +import ConnectionLoader from '../../ConnectDapp/ConnectionLoader' +import { ROUTES, WITNESS_SCOPE } from '../../../core/constants' +import CloseButton from '../../CloseButton' +import FullHeightPanel from '../../Panel/FullHeightPanel' +import WallletConnect from '../../../assets/icons/wallet_connect.svg' +import styles from '../../../containers/ConnectDapp/styles.scss' +import DialogueBox from '../../DialogueBox' +import WarningIcon from '../../../assets/icons/warning.svg' +import Confirm from '../../../assets/icons/confirm_connection.svg' +import Deny from '../../../assets/icons/deny_connection.svg' +import Tooltip from '../../Tooltip' +import Info from '../../../assets/icons/info.svg' + +import { getNode, getRPCEndpoint } from '../../../actions/nodeStorageActions' +import Invocation from '../components/Invocation' +import MessageSuccess from '../MessageSuccess' + +const electron = require('electron') + +type Props = { + request: TSessionRequest, + session: TSession, + isHardwareLogin: boolean, + publicKey: string, + history: any, + showSuccessNotification({ message: string }): any, + showInfoNotification({ message: string, autoDismiss?: number }): any, + hideNotification(id: string): any, + theme: string, + net: string, +} + +const SignTransaction = ({ + request, + session, + isHardwareLogin, + history, + publicKey, + showSuccessNotification, + showInfoNotification, + hideNotification, + theme, + net, +}: Props) => { + const requestParams = useMemo(() => request.params.request.params, [request]) + + const { rejectRequest, approveRequest } = useWalletConnectWallet() + const [loading, setLoading] = useState(false) + const [fee, setFee] = useState() + const [success, setSuccess] = useState(false) + + const handleCalculateFee = useCallback( + async () => { + try { + setLoading(true) + + if ( + requestParams.extraNetworkFee || + requestParams.extraSystemFee || + requestParams.networkFeeOverride || + requestParams.systemFeeOverride + ) { + showInfoNotification({ + message: 'The dApp has overwritten the fees', + }) + } + + const account = new n3Wallet.Account(publicKey) + let rpcAddress = await getNode(net) + if (!rpcAddress) { + rpcAddress = await getRPCEndpoint(net) + } + + const invoker = await NeonInvoker.init({ rpcAddress, account }) + const { total } = await invoker.calculateFee(requestParams) + + setFee(total) + } finally { + setLoading(false) + } + }, + [net, publicKey, requestParams, showInfoNotification], + ) + + const reject = () => { + rejectRequest(request) + showSuccessNotification({ + message: `You have denied request from ${session.peer.metadata.name}.`, + }) + history.push(ROUTES.DASHBOARD) + } + + const approve = async () => { + try { + setLoading(true) + + let notificationId + + if (isHardwareLogin) { + notificationId = showInfoNotification({ + message: 'Please sign the transaction on your hardware device', + autoDismiss: 0, + }) + } + + const { result } = await approveRequest(request) + console.warn(result) + if (notificationId) { + hideNotification(notificationId) + } + + setSuccess(true) + } finally { + setLoading(false) + } + } + + useEffect( + () => { + handleCalculateFee() + }, + [handleCalculateFee], + ) + + return loading && !success ? ( + + ) : success ? ( + + ) : ( + ( + + )} + renderHeaderIcon={() => ( +
+ +
+ )} + renderInstructions={false} + className={styles.approveTransactionPanel} + containerClassName={styles.approveTransactionPanelContainer} + > +
+ + +

+ {session.peer.metadata.name} wants to call +

+ + {isHardwareLogin && ( + + } + renderText={() => ( +
+ To sign this transaction with your ledger, enable custom + contract data in the Neo N3 app settings. Read more about how to + enable this setting + { + electron.shell.openExternal( + 'https://medium.com/proof-of-working/signing-custom-transactions-with-ledger-29723f6eaa4', + ) + }} + > + here + . +
+ )} + className={styles.warningDialogue} + /> + )} + + {request.params.request.params.invocations.map((invocation, index) => ( + + ))} + + {request?.params?.request?.params?.signers?.length <= 1 && ( +
+ + {request.params.request.params.signers.length ? ( +
+ { + WITNESS_SCOPE[ + String(request.params.request.params.signers[0]?.scopes) + ] + } + {WITNESS_SCOPE[ + String(request.params.request.params.signers[0]?.scopes) + ] === 'Global' && } +
+ ) : ( +
{WITNESS_SCOPE['1']}
+ )} +
+ )} + +
+ +
+ {fee} GAS + + + +
+
+ +
+ Please confirm you would like to proceed +
+ + + +
+
+
+
+ ) +} + +export default SignTransaction diff --git a/app/components/DappRequest/SignTransaction/index.js b/app/components/DappRequest/SignTransaction/index.js new file mode 100644 index 000000000..54a51fd0f --- /dev/null +++ b/app/components/DappRequest/SignTransaction/index.js @@ -0,0 +1,35 @@ +// @flow +import { withRouter } from 'react-router-dom' +import { compose } from 'recompose' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +import SignTransaction from './SignTransaction' +import { + showSuccessNotification, + showInfoNotification, + hideNotification, +} from '../../../modules/notifications' +import withAuthData from '../../../hocs/withAuthData' +import withThemeData from '../../../hocs/withThemeData' +import withNetworkData from '../../../hocs/withNetworkData' + +const actionCreators = { + showSuccessNotification, + showInfoNotification, + hideNotification, +} + +const mapDispatchToProps = dispatch => + bindActionCreators(actionCreators, dispatch) + +export default compose( + connect( + null, + mapDispatchToProps, + ), + withAuthData(), + withThemeData(), + withNetworkData(), + withRouter, +)(SignTransaction) diff --git a/app/components/DappRequest/InvokeFunction/Invocation.jsx b/app/components/DappRequest/components/Invocation.jsx similarity index 100% rename from app/components/DappRequest/InvokeFunction/Invocation.jsx rename to app/components/DappRequest/components/Invocation.jsx diff --git a/app/containers/DappRequest/DappRequest.jsx b/app/containers/DappRequest/DappRequest.jsx index 812bd1689..1737fcb05 100644 --- a/app/containers/DappRequest/DappRequest.jsx +++ b/app/containers/DappRequest/DappRequest.jsx @@ -9,6 +9,7 @@ import SignMessage from '../../components/DappRequest/SignMessage' import Encrypt from '../../components/DappRequest/Encrypt' import Decrypt from '../../components/DappRequest/Decrypt' import DecryptFromArray from '../../components/DappRequest/DecryptFromArray' +import SignTransaction from '../../components/DappRequest/SignTransaction' type Props = { history: any, @@ -22,6 +23,7 @@ const componentsByMethod: { [key: string]: any } = { encrypt: Encrypt, decrypt: Decrypt, decryptFromArray: DecryptFromArray, + signTransaction: SignTransaction, } const DappRequest = ({ history, showErrorNotification }: Props) => { diff --git a/app/util/walletConnect.js b/app/util/walletConnect.js index d2f9ce361..59a6518fc 100644 --- a/app/util/walletConnect.js +++ b/app/util/walletConnect.js @@ -187,6 +187,7 @@ export const walletConnectOptions: TOptions = { 'encrypt', 'decryptFromArray', 'calculateFee', + 'signTransaction', ], autoAcceptMethods: [ 'testInvoke', diff --git a/package.json b/package.json index 7c3ed3500..c8cbf60f3 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,7 @@ "@cityofzion/bs-neo-legacy": "0.3.0", "@cityofzion/bs-neo3": "0.3.0", "@cityofzion/dora-ts": "^0.0.9", + "@cityofzion/neon-dappkit": "^0.3.1", "@cityofzion/neon-invoker": "1.5.2", "@cityofzion/neon-js": "5.3.0", "@cityofzion/neon-js-legacy": "npm:@cityofzion/neon-js@3.11.9", @@ -315,4 +316,4 @@ "setupTestFrameworkScriptFile": "/__tests__/setupTests.js", "testURL": "http://localhost" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index ed833317e..9efcb554a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2198,6 +2198,11 @@ resolved "https://registry.yarnpkg.com/@cityofzion/neon-dappkit-types/-/neon-dappkit-types-0.3.0.tgz#6c04cd8db94c3ad87b435ddfd8ad927daba1c484" integrity sha512-kWBtU2UYkESj1QxmFxjMwzB0RwBVdwKYrnwHqN+wv43ceZ3d2CCdprFsHvqQoo2DNpSKPgmq6RIlnhedBH7Shg== +"@cityofzion/neon-dappkit-types@0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@cityofzion/neon-dappkit-types/-/neon-dappkit-types-0.3.1.tgz#b0c58c68e8406b7cc00ae2a84d15d264096ee950" + integrity sha512-bQA/EBQUU6ZPmDARXB0w6I2iJqdo4S2s3xERPN91wCHd9WMkCIrtec269X1Yjd9qGoVibLMyA3xjqrOTyUBpxw== + "@cityofzion/neon-dappkit@0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@cityofzion/neon-dappkit/-/neon-dappkit-0.3.0.tgz#0bb3b7baf7343f07d0c935cdbb5e1d546274918f" @@ -2209,6 +2214,21 @@ elliptic "^6.5.4" randombytes "^2.1.0" +"@cityofzion/neon-dappkit@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@cityofzion/neon-dappkit/-/neon-dappkit-0.3.1.tgz#1e38e6d7650e1a967ea9972a929c826247ddc966" + integrity sha512-MyeOZu9IIGwboD82vLte6F645FTAYzevik0mh5lH0mNhVuaP5hjwcfDCDHs+WfQKwp5w7FA0RshiKRqm3MEeVw== + dependencies: + "@cityofzion/neon-dappkit-types" "0.3.1" + "@cityofzion/neon-js" "5.5.1" + crypto-browserify "^3.12.0" + crypto-js "^4.1.1" + elliptic "^6.5.4" + randombytes "^2.1.0" + readable-stream "^4.4.2" + stream "^0.0.2" + stream-browserify "^3.0.0" + "@cityofzion/neon-invoker@1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@cityofzion/neon-invoker/-/neon-invoker-1.4.0.tgz#a3177ad66c4b84bb57228418f5c451bc901c1549" @@ -3691,7 +3711,7 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -abort-controller@3.0.0: +abort-controller@3.0.0, abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== @@ -5415,7 +5435,7 @@ buffer@5.0.8: base64-js "^1.0.2" ieee754 "^1.1.4" -buffer@6.0.3: +buffer@6.0.3, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -6662,11 +6682,16 @@ crypto-js@4.0.0: resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.0.0.tgz#2904ab2677a9d042856a2ea2ef80de92e4a36dcc" integrity sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg== -crypto-js@4.1.1, crypto-js@^4.1.1: +crypto-js@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== +crypto-js@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + crypto-random-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" @@ -7715,6 +7740,11 @@ elliptic@6.5.4, elliptic@^6.5.3, elliptic@^6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +emitter-component@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/emitter-component/-/emitter-component-1.1.1.tgz#065e2dbed6959bf470679edabeaf7981d1003ab6" + integrity sha512-G+mpdiAySMuB7kesVRLuyvYRqDmshB7ReKEVuyBPkzQlmiDiLrt7hHHIy4Aff552bgknVN7B2/d3lzhGO5dvpQ== + emoji-regex@^7.0.1, emoji-regex@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -10209,7 +10239,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -14672,6 +14702,26 @@ readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^3.5.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.4.2.tgz#e6aced27ad3b9d726d8308515b9a1b98dc1b9d13" + integrity sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + readable-stream@~2.1.5: version "2.1.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" @@ -16104,6 +16154,14 @@ stream-browserify@^2.0.1: inherits "~2.0.1" readable-stream "^2.0.2" +stream-browserify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== + dependencies: + inherits "~2.0.4" + readable-stream "^3.5.0" + stream-each@^1.1.0: version "1.2.3" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" @@ -16128,6 +16186,13 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +stream@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stream/-/stream-0.0.2.tgz#7f5363f057f6592c5595f00bc80a27f5cec1f0ef" + integrity sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g== + dependencies: + emitter-component "^1.1.1" + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -16217,7 +16282,7 @@ string.prototype.trimstart@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" -string_decoder@^1.0.0, string_decoder@^1.1.1: +string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==