Skip to content
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

CU-86a0cm32y - Implement signing the same transaction on multiple wal… #2564

Merged
merged 1 commit into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
262 changes: 262 additions & 0 deletions app/components/DappRequest/SignTransaction/SignTransaction.jsx
Original file line number Diff line number Diff line change
@@ -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 ? (
<ConnectionLoader />
) : success ? (
<MessageSuccess text="You have successfully signed your transaction" />
) : (
<FullHeightPanel
headerText="Wallet Connect"
renderCloseButton={() => (
<CloseButton onClick={reject} routeTo={ROUTES.DASHBOARD} />
)}
renderHeaderIcon={() => (
<div className={styles.walletConnectIcon}>
<WallletConnect />
</div>
)}
renderInstructions={false}
className={styles.approveTransactionPanel}
containerClassName={styles.approveTransactionPanelContainer}
>
<div
className={classNames([
styles.approveConnectionContainer,
styles.approveRequestContainer,
])}
>
<img src={session.peer.metadata.icons[0]} />

<h3 className={styles.wantsToCallHeader}>
{session.peer.metadata.name} wants to call
</h3>

{isHardwareLogin && (
<DialogueBox
icon={
<WarningIcon
className={styles.warningIcon}
height={60}
width={60}
/>
}
renderText={() => (
<div>
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
<a
onClick={() => {
electron.shell.openExternal(
'https://medium.com/proof-of-working/signing-custom-transactions-with-ledger-29723f6eaa4',
)
}}
>
here
</a>.
</div>
)}
className={styles.warningDialogue}
/>
)}

{request.params.request.params.invocations.map((invocation, index) => (
<Invocation
invocation={invocation}
theme={theme}
net={net}
requestParams={requestParams}
key={index}
/>
))}

{request?.params?.request?.params?.signers?.length <= 1 && (
<div
className={classNames([
styles.detailsLabel,
styles.detailRow,
styles.singleSigRow,
])}
>
<label>signature scope</label>
{request.params.request.params.signers.length ? (
<div>
{
WITNESS_SCOPE[
String(request.params.request.params.signers[0]?.scopes)
]
}
{WITNESS_SCOPE[
String(request.params.request.params.signers[0]?.scopes)
] === 'Global' && <WarningIcon />}
</div>
) : (
<div>{WITNESS_SCOPE['1']}</div>
)}
</div>
)}

<div
className={classNames([
styles.detailsLabel,
styles.detailRow,
styles.feeRow,
])}
>
<label>fee</label>
<div className={styles.fee}>
{fee} GAS
<Tooltip title="Other network fees may apply">
<Info />
</Tooltip>
</div>
</div>

<div className={styles.confirmation}>
Please confirm you would like to proceed
<div>
<Confirm onClick={approve} />

<Deny onClick={reject} />
</div>
</div>
</div>
</FullHeightPanel>
)
}

export default SignTransaction
35 changes: 35 additions & 0 deletions app/components/DappRequest/SignTransaction/index.js
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 2 additions & 0 deletions app/containers/DappRequest/DappRequest.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -22,6 +23,7 @@ const componentsByMethod: { [key: string]: any } = {
encrypt: Encrypt,
decrypt: Decrypt,
decryptFromArray: DecryptFromArray,
signTransaction: SignTransaction,
}

const DappRequest = ({ history, showErrorNotification }: Props) => {
Expand Down
1 change: 1 addition & 0 deletions app/util/walletConnect.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ export const walletConnectOptions: TOptions = {
'encrypt',
'decryptFromArray',
'calculateFee',
'signTransaction',
],
autoAcceptMethods: [
'testInvoke',
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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/[email protected]",
Expand Down Expand Up @@ -315,4 +316,4 @@
"setupTestFrameworkScriptFile": "<rootDir>/__tests__/setupTests.js",
"testURL": "http://localhost"
}
}
}
Loading