diff --git a/__tests__/actions/authActions.test.js b/__tests__/actions/authActions.test.js index dc7f73c89..ffac195f0 100644 --- a/__tests__/actions/authActions.test.js +++ b/__tests__/actions/authActions.test.js @@ -1,5 +1,6 @@ import { wifLoginActions } from '../../app/actions/authActions' +jest.setTimeout(10000) describe('authActions', () => { describe('wifLoginActions', () => { describe('call', () => { diff --git a/__tests__/actions/blockHeightActions.test.js b/__tests__/actions/blockHeightActions.test.js index 0c49157f4..cc19d6a21 100644 --- a/__tests__/actions/blockHeightActions.test.js +++ b/__tests__/actions/blockHeightActions.test.js @@ -1,4 +1,4 @@ -import { rpc } from '@cityofzion/neon-js' +import { rpc } from '@cityofzion/neon-js-legacy' import main, { blockHeightActions, diff --git a/__tests__/actions/claimsActions.test.js b/__tests__/actions/claimsActions.test.js index 6c93e7364..86b2b529b 100644 --- a/__tests__/actions/claimsActions.test.js +++ b/__tests__/actions/claimsActions.test.js @@ -1,5 +1,5 @@ /* eslint-disable */ -import { api, u } from '@cityofzion/neon-js' +import { api, u } from '@cityofzion/neon-js-legacy' import claimsActions from '../../app/actions/claimsActions' import { mockPromiseResolved } from '../testHelpers' diff --git a/__tests__/actions/nodeStorageActions.test.js b/__tests__/actions/nodeStorageActions.test.js index 00fb3e678..ab396f297 100644 --- a/__tests__/actions/nodeStorageActions.test.js +++ b/__tests__/actions/nodeStorageActions.test.js @@ -1,4 +1,4 @@ -import { api } from '@cityofzion/neon-js' +import { api } from '@cityofzion/neon-js-legacy' import nock from 'nock' import { ipcRenderer } from 'electron' diff --git a/__tests__/components/Logout.test.js b/__tests__/components/Logout.test.js index 6a5c71c78..0d459b9e7 100644 --- a/__tests__/components/Logout.test.js +++ b/__tests__/components/Logout.test.js @@ -2,6 +2,8 @@ import React from 'react' import { shallow } from 'enzyme' import Logout from '../../app/containers/App/Sidebar/Logout/Logout' +import { WalletConnectWalletProvider } from '@cityofzion/wallet-connect-sdk-wallet-react' +import { walletConnectOptions } from '../../app/util/walletConnect' describe('Logout', () => { const logout = jest.fn() diff --git a/__tests__/setupTests.js b/__tests__/setupTests.js index a873322e0..17d719d8a 100644 --- a/__tests__/setupTests.js +++ b/__tests__/setupTests.js @@ -2,7 +2,7 @@ import 'raf/polyfill' import { configure } from 'enzyme' import Adapter from '@cfaester/enzyme-adapter-react-18' import nock from 'nock' -import { api } from '@cityofzion/neon-js' +import { api } from '@cityofzion/neon-js-legacy' import { TextEncoder, TextDecoder } from 'util' global.TextEncoder = TextEncoder diff --git a/app/actions/authActions.js b/app/actions/authActions.js index 94260224a..9a4474b54 100644 --- a/app/actions/authActions.js +++ b/app/actions/authActions.js @@ -1,6 +1,6 @@ // @flow -import { wallet } from '@cityofzion/neon-js' -import { wallet as n3Wallet } from '@cityofzion/neon-js-next' +import { wallet } from '@cityofzion/neon-js-legacy' +import { wallet as n3Wallet } from '@cityofzion/neon-js' import { noop } from 'lodash-es' import { createActions } from 'spunky' import dns from 'dns' diff --git a/app/actions/balancesActions.js b/app/actions/balancesActions.js index 0f151c863..783fc4cb5 100644 --- a/app/actions/balancesActions.js +++ b/app/actions/balancesActions.js @@ -1,6 +1,6 @@ // @flow -import { api, u, rpc, sc, wallet } from '@cityofzion/neon-js' -import { rpc as n3Rpc } from '@cityofzion/neon-js-next' +import { api, u, rpc, sc, wallet } from '@cityofzion/neon-js-legacy' +import { rpc as n3Rpc } from '@cityofzion/neon-js' import { extend, isEmpty, get } from 'lodash-es' import { createActions } from 'spunky' import { Howl } from 'howler' diff --git a/app/actions/blockHeightActions.js b/app/actions/blockHeightActions.js index 245af1eac..5edfa7dcf 100644 --- a/app/actions/blockHeightActions.js +++ b/app/actions/blockHeightActions.js @@ -1,5 +1,5 @@ // @flow -import { rpc } from '@cityofzion/neon-js' +import { rpc } from '@cityofzion/neon-js-legacy' import { createActions } from 'spunky' import { isEmpty } from 'lodash-es' import { getNode, getRPCEndpoint } from './nodeStorageActions' diff --git a/app/actions/claimsActions.js b/app/actions/claimsActions.js index 7010a128d..880e0188c 100644 --- a/app/actions/claimsActions.js +++ b/app/actions/claimsActions.js @@ -1,6 +1,6 @@ // @flow import { api } from '@cityofzion/neon-js-legacy-latest' -import { rpc as n3Rpc, u as n3U } from '@cityofzion/neon-js-next' +import { rpc as n3Rpc, u as n3U } from '@cityofzion/neon-js' import { createActions } from 'spunky' import { getNode, getRPCEndpoint } from './nodeStorageActions' diff --git a/app/actions/icoTokensActions.js b/app/actions/icoTokensActions.js index 61b9633c5..21f06dddb 100644 --- a/app/actions/icoTokensActions.js +++ b/app/actions/icoTokensActions.js @@ -1,5 +1,5 @@ // @flow -import { api } from '@cityofzion/neon-js' +import { api } from '@cityofzion/neon-js-legacy' import { createActions } from 'spunky' import { getNode, getRPCEndpoint } from './nodeStorageActions' diff --git a/app/actions/nftActions.js b/app/actions/nftActions.js index 1b8ac8365..521b1d8d6 100644 --- a/app/actions/nftActions.js +++ b/app/actions/nftActions.js @@ -1,8 +1,8 @@ // @flow import { createActions } from 'spunky' import axios from 'axios' -import { rpc } from '@cityofzion/neon-js' -import { rpc as n3Rpc } from '@cityofzion/neon-js-next' +import { rpc } from '@cityofzion/neon-js-legacy' +import { rpc as n3Rpc } from '@cityofzion/neon-js' import { getSettings } from './settingsActions' import { getNode, getRPCEndpoint } from './nodeStorageActions' diff --git a/app/actions/nodeNetworkActions.js b/app/actions/nodeNetworkActions.js index 68669b710..257190a54 100644 --- a/app/actions/nodeNetworkActions.js +++ b/app/actions/nodeNetworkActions.js @@ -1,5 +1,5 @@ // @flow -import { rpc, settings } from '@cityofzion/neon-js' +import { rpc, settings } from '@cityofzion/neon-js-legacy' import { createActions } from 'spunky' import { diff --git a/app/actions/nodeStorageActions.js b/app/actions/nodeStorageActions.js index 329cc708a..24f1770bc 100644 --- a/app/actions/nodeStorageActions.js +++ b/app/actions/nodeStorageActions.js @@ -1,7 +1,7 @@ // @flow import { createActions } from 'spunky' import { random, get, compact } from 'lodash-es' -import { rpc, api, settings } from '@cityofzion/neon-js' +import { rpc, api, settings } from '@cityofzion/neon-js-legacy' import { getStorage, setStorage } from '../core/storage' import { diff --git a/app/actions/pendingTransactionActions.js b/app/actions/pendingTransactionActions.js index 827762615..d1825d61e 100644 --- a/app/actions/pendingTransactionActions.js +++ b/app/actions/pendingTransactionActions.js @@ -1,6 +1,6 @@ // @flow import { createActions } from 'spunky' -import Neon, { rpc } from '@cityofzion/neon-js' +import Neon, { rpc } from '@cityofzion/neon-js-legacy' import { isEmpty } from 'lodash-es' import { toBigNumber } from '../core/math' diff --git a/app/actions/transactionHistoryActions.js b/app/actions/transactionHistoryActions.js index 255a71d74..6a4788293 100644 --- a/app/actions/transactionHistoryActions.js +++ b/app/actions/transactionHistoryActions.js @@ -1,7 +1,7 @@ // @flow import { NeoLegacyREST, NeoRest } from '@cityofzion/dora-ts/dist/api' import { createActions } from 'spunky' -import { rpc as n3Rpc, sc, u } from '@cityofzion/neon-js-next' +import { rpc as n3Rpc, sc, u } from '@cityofzion/neon-js' import axios from 'axios' import { TX_TYPES } from '../core/constants' diff --git a/app/actions/voteActions.js b/app/actions/voteActions.js index 72e940d3d..d26c94c03 100644 --- a/app/actions/voteActions.js +++ b/app/actions/voteActions.js @@ -1,5 +1,5 @@ // @flow -import { api, rpc } from '@cityofzion/neon-js' +import { api, rpc } from '@cityofzion/neon-js-legacy' import { createActions } from 'spunky' import { getNetworkById } from '../core/deprecated' diff --git a/app/components/ConnectDapp/ApproveConnection.js b/app/components/ConnectDapp/ApproveConnection.js deleted file mode 100644 index 8554805b8..000000000 --- a/app/components/ConnectDapp/ApproveConnection.js +++ /dev/null @@ -1,202 +0,0 @@ -// @flow -import React from 'react' -import { SessionTypes } from '@walletconnect/types' -import { compose } from 'recompose' -import { connect } from 'react-redux' - -import { useWalletConnect } from '../../context/WalletConnect/WalletConnectContext' -import { DEFAULT_NAMESPACES, ROUTES, MODAL_TYPES } 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 Confirm from '../../assets/icons/confirm_connection.svg' -import Deny from '../../assets/icons/deny_connection.svg' -import { showModal } from '../../modules/modal' - -const ApproveConnection = ({ - proposal, - resetState, - history, - showSuccessNotification, - showErrorNotification, - net, - address, - showNetworkSwitchModal, -}: { - proposal: SessionTypes.Proposal, - resetState: () => any, - history: any, - showSuccessNotification: ({ message: string }) => any, - showErrorNotification: ({ message: string }) => any, - net: string, - address: string, - showNetworkSwitchModal: ({ - dAppName: string, - proposedNetwork: string, - approveSession: (network?: string) => void, - rejectSession: () => void, - }) => void, -}) => { - const walletConnectCtx = useWalletConnect() - const { - proposer: { metadata }, - requiredNamespaces, - } = proposal.params - - const approveSession = network => { - walletConnectCtx - .approveSession( - proposal, - [ - { - address, - chain: `neo3:${ - network || net === 'Custom' ? 'private' : net.toLowerCase() - }`, - }, - ], - DEFAULT_NAMESPACES, - ) - .catch(e => { - // TODO: parse into a more user friendly error - showErrorNotification({ - message: `An error occurred attempting to approve session: ${e}`, - }) - }) - - showSuccessNotification({ - message: `You have accepted connection from ${ - proposal ? metadata.name : 'unknown dApp' - }.`, - }) - history.push(ROUTES.DASHBOARD) - } - - const rejectSession = () => { - walletConnectCtx.rejectSession(proposal) - showSuccessNotification({ - message: `You have rejected connection from ${ - proposal ? metadata.name : 'unknown dApp' - }.`, - }) - resetState() - history.push(ROUTES.DASHBOARD) - } - - const handleApproveSessionClick = () => { - // 'neo3:testnet' or 'neo3:mainnet' - const proposedNetwork = proposal.params.requiredNamespaces.neo3.chains[0] - - if (net === 'Custom' && proposedNetwork === 'neo3:private') { - return approveSession() - } - - const currentNetwork = `neo3:${ - net === 'Custom' ? 'private' : net.toLowerCase() - }` - - if ( - proposedNetwork !== currentNetwork && - proposedNetwork !== 'neo3:private' - ) { - return showNetworkSwitchModal({ - dAppName: metadata.name, - approveSession, - rejectSession, - proposedNetwork: proposal.params.requiredNamespaces.neo3.chains[0], - }) - } - - if ( - proposedNetwork === 'neo3:private' && - proposedNetwork !== currentNetwork - ) { - return showErrorNotification({ - message: `${metadata.name || - 'unknown dApp'} is attempting to connect to a private network but you are currently on ${net}.`, - }) - } - - approveSession() - } - - return ( - ( - { - walletConnectCtx.rejectSession(proposal) - resetState() - }} - routeTo={ROUTES.DASHBOARD} - /> - )} - renderHeaderIcon={() => ( -
- -
- )} - renderInstructions={false} - > -
- - -

{metadata.name} wants to connect

-
- {metadata.name} wants to connect to your wallet -
-
- -
-
-
- - {/* $FlowFixMe */} - {Object.values(requiredNamespaces).map(({ chains }, i) => ( -
{chains.join('')}
- ))} -
-
- - {/* $FlowFixMe */} - {Object.values(requiredNamespaces).map(({ methods }) => - methods.join(', '), - )} -
-
-
-
- Please confirm you would like to connect -
- { - handleApproveSessionClick() - }} - /> - - { - rejectSession() - }} - /> -
-
-
-
-
- ) -} - -const mapDispatchToProps = dispatch => ({ - showNetworkSwitchModal: props => - dispatch(showModal(MODAL_TYPES.NETWORK_SWITCH, props)), -}) - -export default compose( - connect( - null, - mapDispatchToProps, - ), -)(ApproveConnection) diff --git a/app/components/ConnectDapp/ApproveConnection/ApproveConnection.jsx b/app/components/ConnectDapp/ApproveConnection/ApproveConnection.jsx new file mode 100644 index 000000000..7b72ac837 --- /dev/null +++ b/app/components/ConnectDapp/ApproveConnection/ApproveConnection.jsx @@ -0,0 +1,170 @@ +// @flow +import React from 'react' + +import { + useWalletConnectWallet, + TSessionProposal, +} from '@cityofzion/wallet-connect-sdk-wallet-react' +import { MODAL_TYPES, ROUTES } 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 Confirm from '../../../assets/icons/confirm_connection.svg' +import Deny from '../../../assets/icons/deny_connection.svg' +import { + convertChain, + getNetworkFromProposal, +} from '../../../util/walletConnect' + +type Props = { + proposal: TSessionProposal, + history: any, + showSuccessNotification: ({ message: string }) => void, + showErrorNotification: ({ message: string }) => void, + showModal: (modalType: string, modalProps: Object) => void, + net: string, + address: string, +} + +const ApproveConnection = ({ + proposal, + history, + showSuccessNotification, + showErrorNotification, + net, + address, + showModal, +}: Props) => { + const { approveProposal, rejectProposal } = useWalletConnectWallet() + const { + proposer: { metadata }, + requiredNamespaces, + } = proposal.params + + const approveSession = async (network: string) => { + const chain = convertChain(network) + + try { + await approveProposal(proposal, { account: { address, chain } }) + + showSuccessNotification({ + message: `You have accepted connection from ${ + proposal ? metadata.name : 'unknown dApp' + }.`, + stack: true, + }) + } catch (error) { + showErrorNotification({ + message: `An error occurred attempting to approve proposal: ${ + error.message + }`, + stack: true, + }) + } finally { + history.push(ROUTES.DASHBOARD) + } + } + + const rejectSession = async (error?: string) => { + try { + await rejectProposal(proposal) + if (error) { + showErrorNotification({ + message: error, + }) + } else { + showSuccessNotification({ + message: `You have rejected connection from ${metadata.name}.`, + stack: true, + }) + } + } catch (error) { + showErrorNotification({ + message: `An error occurred attempting to reject proposal: ${ + error.message + }`, + }) + } finally { + history.push(ROUTES.DASHBOARD) + } + } + + const handleApproveSessionClick = () => { + const { network: proposalNetwork } = getNetworkFromProposal(proposal) + + if (proposalNetwork !== net) { + return showModal(MODAL_TYPES.NETWORK_SWITCH, { + dAppName: metadata.name, + onSwitch: () => approveSession(proposalNetwork), + onCancel: rejectSession, + proposalNetwork, + }) + } + + approveSession(net) + } + + return ( + ( + + )} + renderHeaderIcon={() => ( +
+ +
+ )} + renderInstructions={false} + > +
+ + +

{metadata.name} wants to connect

+
+ {metadata.name} wants to connect to your wallet +
+
+ +
+
+
+ + {/* $FlowFixMe */} + {Object.values(requiredNamespaces).map(({ chains }, i) => ( +
{chains.join('')}
+ ))} +
+
+ + {/* $FlowFixMe */} + {Object.values(requiredNamespaces).map(({ methods }) => + methods.join(', '), + )} +
+
+
+
+ Please confirm you would like to connect +
+ { + handleApproveSessionClick() + }} + /> + + { + rejectSession() + }} + /> +
+
+
+
+
+ ) +} + +export default ApproveConnection diff --git a/app/components/ConnectDapp/ApproveConnection/index.js b/app/components/ConnectDapp/ApproveConnection/index.js new file mode 100644 index 000000000..000706f44 --- /dev/null +++ b/app/components/ConnectDapp/ApproveConnection/index.js @@ -0,0 +1,31 @@ +import { compose } from 'recompose' +import { connect } from 'react-redux' +import { withRouter } from 'react-router-dom' +import { bindActionCreators } from 'redux' +import { showModal } from '../../../modules/modal' +import ApproveConnection from './ApproveConnection' +import withAuthData from '../../../hocs/withAuthData' +import withNetworkData from '../../../hocs/withNetworkData' +import { + showErrorNotification, + showSuccessNotification, +} from '../../../modules/notifications' + +const actionCreators = { + showModal, + showErrorNotification, + showSuccessNotification, +} + +const mapDispatchToProps = dispatch => + bindActionCreators(actionCreators, dispatch) + +export default compose( + connect( + null, + mapDispatchToProps, + ), + withRouter, + withAuthData(), + withNetworkData(), +)(ApproveConnection) diff --git a/app/components/ConnectDapp/ApproveTransaction.js b/app/components/ConnectDapp/ApproveTransaction.js deleted file mode 100644 index c218c2e43..000000000 --- a/app/components/ConnectDapp/ApproveTransaction.js +++ /dev/null @@ -1,484 +0,0 @@ -// @flow - -import React from 'react' -import classNames from 'classnames' -import { JsonRpcRequest } from '@json-rpc-tools/utils' - -import { useWalletConnect } from '../../context/WalletConnect/WalletConnectContext' -import { ROUTES } 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 { TX_STATE_TYPE_MAPPINGS } from '../../containers/ConnectDapp/mocks' -import CopyToClipboard from '../CopyToClipboard' -import Tooltip from '../Tooltip' -import DoraIcon from '../../assets/icons/dora_icon_light.svg' -import DoraIconDark from '../../assets/icons/dora_icon_dark.svg' -import Info from '../../assets/icons/info.svg' -import Up from '../../assets/icons/chevron-up.svg' -import Down from '../../assets/icons/chevron-down.svg' - -const electron = require('electron') - -const WITNESS_SCOPE = { - '0': 'None', - '1': 'CalledByEntry', - '16': 'CustomContracts', - '32': 'CustomGroups', - '128': 'Global', -} - -const ApproveTransaction = ({ - request, - peer, - isHardwareLogin, - resetState, - history, - showSuccessNotification, - setLoading, - loading, - fee, - theme, - requestParamsVisible, - setRequestParamsVisible, - net, -}: { - request: JsonRpcRequest, - peer: any, - isHardwareLogin: boolean, - resetState: () => any, - history: any, - showSuccessNotification: ({ message: string }) => any, - setLoading: boolean => any, - loading: boolean, - fee: string, - theme: string, - requestParamsVisible: { [key: number]: boolean }, - setRequestParamsVisible: ({ [key: number]: boolean }) => any, - net: string, -}) => { - const walletConnectCtx = useWalletConnect() - - const shouldDisplayReqParams = invocation => !!invocation.args.length - - const getParamDefinitions = invocation => { - if (invocation.contract && invocation.contract.abi) { - return invocation.contract.abi.methods.find( - method => method.name === invocation.operation, - ).parameters - } - return invocation.args.map((arg, i) => ({ name: i, type: arg.type })) - } - - const renderParam = (arg: any, definition: any, i: number) => { - function mapArrayArg(argValue: any, index: number) { - return ( -
-
-
{index}
- {argValue && argValue.value} - -
- -
- {argValue?.type} -
-
- ) - } - - return ( -
-
- {' '} - {definition?.name ?? null} -
-
- {arg.type !== 'Array' && ( - - {arg.value || 'null'} - - - )} - {arg.type === 'Array' && - arg.value.map( - (arg, i) => - arg.type === 'Array' - ? renderParam(arg, { name: i, type: arg.type }, i) - : mapArrayArg(arg, i), - )} -
-
- {definition?.type} -
-
- ) - } - - const handleOpenDoraLink = hash => { - if (hash) { - return electron.shell.openExternal( - net === 'MainNet' - ? `https://dora.coz.io/contract/neo3/mainnet/${hash}` - : `https://dora.coz.io/contract/neo3/testnet/${hash}`, - ) - } - return null - } - - return ( - ( - { - walletConnectCtx.rejectRequest(request) - resetState() - history.push(ROUTES.DASHBOARD) - }} - /> - )} - renderHeaderIcon={() => ( -
- -
- )} - renderInstructions={false} - className={styles.approveTransactionPanel} - containerClassName={styles.approveTransactionPanelContainer} - > -
- - -

- {peer && 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 && - request.params.request.params.invocations.map((invocation, i) => ( - -
-
- {invocation.contract?.name} -
-
- -
-
- -
- {invocation.scriptHash}{' '} - {theme === 'Light' ? ( - - handleOpenDoraLink(invocation.scriptHash) - } - /> - ) : ( - - handleOpenDoraLink(invocation.scriptHash) - } - /> - )} -
-
- -
- -
{invocation.operation}
-
- {shouldDisplayReqParams(invocation) ? ( -
-
- setRequestParamsVisible({ - ...requestParamsVisible, - [i]: !requestParamsVisible[i], - }) - } - > - - -
{requestParamsVisible[i] ? : }
-
- {requestParamsVisible[i] && ( -
- {invocation.args.map((p: any, i: number) => { - const paramDefinitions = getParamDefinitions( - invocation, - ) - return renderParam(p, paramDefinitions[i], i) - })} -
- )} -
- ) : null} - {request?.params?.request?.params?.signers?.length > 1 && ( -
- - -
- {request.params.request.params.signers.map( - (signer, i) => ( -
-
-
{i}
- - {WITNESS_SCOPE[String(signer.scopes)]} - {WITNESS_SCOPE[String(signer.scopes)] === - 'Global' && } -
-
- -
- {signer.account}{' '} - {theme === 'Light' ? ( - - handleOpenDoraLink(signer.account) - } - /> - ) : ( - - handleOpenDoraLink(signer.account) - } - /> - )} -
-
- - {/* $FlowFixMe */} - {signer.allowedContracts?.length && - /* $FlowFixMe */ - signer.allowedContracts?.map(contract => ( -
- -
- {contract}{' '} - {theme === 'Light' ? ( - - handleOpenDoraLink(contract) - } - /> - ) : ( - - handleOpenDoraLink(contract) - } - /> - )} -
-
- ))} -
- ), - )} -
-
- )} -
-
-
- ))} - {request?.params?.request?.params?.signers?.length > 1 ? null : ( -
- - {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 -
- { - if (!loading) { - setLoading(true) - await walletConnectCtx.approveRequest(request) - setLoading(false) - } - }} - /> - - { - if (!loading) { - showSuccessNotification({ - message: `You have denied request from ${ - peer ? peer.metadata.name : 'unknown dApp' - }.`, - }) - walletConnectCtx.rejectRequest(request) - resetState() - history.push(ROUTES.DASHBOARD) - } - }} - /> -
-
-
-
- ) -} - -export default ApproveTransaction diff --git a/app/components/ConnectDapp/ConnectionError.js b/app/components/ConnectDapp/ConnectionError.js deleted file mode 100644 index 0400990aa..000000000 --- a/app/components/ConnectDapp/ConnectionError.js +++ /dev/null @@ -1,49 +0,0 @@ -// @flow - -import React from 'react' -import { useWalletConnect } from '../../context/WalletConnect/WalletConnectContext' -import { ROUTES } 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 ErrorIcon from '../../assets/icons/wc-error.svg' - -const ConnectionError = ({ resetState }: { resetState: () => any }) => { - const walletConnectCtx = useWalletConnect() - const { error } = walletConnectCtx - return ( - ( - { - walletConnectCtx.setError(undefined) - resetState() - }} - /> - )} - renderHeaderIcon={() => ( -
- -
- )} - renderInstructions={false} - > -
- -

Transaction failed!

-

- {typeof error === 'string' - ? error - : 'An unknown error occurred please try again.'} -

-
-
-
-
- ) -} - -export default ConnectionError diff --git a/app/components/ConnectDapp/ConnectionLoader.js b/app/components/ConnectDapp/ConnectionLoader.js index d7b104a5b..fbfaefce4 100644 --- a/app/components/ConnectDapp/ConnectionLoader.js +++ b/app/components/ConnectDapp/ConnectionLoader.js @@ -1,32 +1,21 @@ // @flow import React from 'react' -import { useWalletConnect } from '../../context/WalletConnect/WalletConnectContext' import { ROUTES } from '../../core/constants' import CloseButton from '../CloseButton' import Loader from '../Loader' import FullHeightPanel from '../Panel/FullHeightPanel' -const ConnectionLoader = () => { - const walletConnectCtx = useWalletConnect() - return ( - ( - { - walletConnectCtx.setError(undefined) - }} - /> - )} - renderHeaderIcon={() => null} - renderInstructions={false} - > -
- -
-
- ) -} +const ConnectionLoader = () => ( + } + renderHeaderIcon={() => null} + renderInstructions={false} + > +
+ +
+
+) export default ConnectionLoader diff --git a/app/components/ConnectDapp/ConnectionUrlForm.js b/app/components/ConnectDapp/ConnectionUrlForm.js deleted file mode 100644 index a11b7aeda..000000000 --- a/app/components/ConnectDapp/ConnectionUrlForm.js +++ /dev/null @@ -1,72 +0,0 @@ -// @flow - -import React from 'react' - -import { useWalletConnect } from '../../context/WalletConnect/WalletConnectContext' -import { ROUTES } 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 ErrorIcon from '../../assets/icons/wc-error.svg' -import TextInput from '../Inputs/TextInput' -import Button from '../Button' -import LockIcon from '../../assets/icons/add.svg' - -const ConnectionUrlForm = ({ - loading, - handleWalletConnectURLSubmit, - setConnectionUrl, - connectionUrl, -}: { - loading: boolean, - handleWalletConnectURLSubmit: () => any, - setConnectionUrl: string => any, - connectionUrl: string, -}) => { - const renderInstructions = () => ( -

- All transactions requested by a connected Dapp will be presented for - signing before being broadcast to the blockchain. No action from the Dapp - will happen without your direct approval. -

- ) - - return ( - } - renderHeaderIcon={() => ( -
- -
- )} - renderInstructions={renderInstructions} - > -
handleWalletConnectURLSubmit()} - > - setConnectionUrl(e.target.value)} - error={null} - /> - - -
- ) -} - -export default ConnectionUrlForm diff --git a/app/components/ConnectDapp/ConnectionUrlForm/ConnectionUrlForm.jsx b/app/components/ConnectDapp/ConnectionUrlForm/ConnectionUrlForm.jsx new file mode 100644 index 000000000..1fdd71844 --- /dev/null +++ b/app/components/ConnectDapp/ConnectionUrlForm/ConnectionUrlForm.jsx @@ -0,0 +1,72 @@ +// @flow +import React, { useState } from 'react' + +import { ROUTES } from '../../../core/constants' +import CloseButton from '../../CloseButton' +import FullHeightPanel from '../../Panel/FullHeightPanel' +import styles from '../../../containers/ConnectDapp/styles.scss' +import TextInput from '../../Inputs/TextInput' +import Button from '../../Button' +import LockIcon from '../../../assets/icons/add.svg' + +type Props = { + onURI: (uri: string) => Promise, +} + +const ConnectionUrlForm = ({ onURI }: Props) => { + const [url, setUrl] = useState('') + const [buttonDisabled, setButtonDisabled] = useState(false) + + async function handleSubmit() { + if (buttonDisabled) return + + setButtonDisabled(true) + try { + await onURI(url) + } finally { + setButtonDisabled(false) + } + } + + const renderInstructions = () => ( +

+ All transactions requested by a connected Dapp will be presented for + signing before being broadcast to the blockchain. No action from the Dapp + will happen without your direct approval. +

+ ) + + return ( + } + renderHeaderIcon={() => ( +
+ +
+ )} + renderInstructions={renderInstructions} + > +
+ setUrl(e.target.value)} + error={null} + /> + + +
+ ) +} + +export default ConnectionUrlForm diff --git a/app/components/ConnectDapp/ConnectionUrlForm/index.js b/app/components/ConnectDapp/ConnectionUrlForm/index.js new file mode 100644 index 000000000..262b54c91 --- /dev/null +++ b/app/components/ConnectDapp/ConnectionUrlForm/index.js @@ -0,0 +1 @@ +export { default } from './ConnectionUrlForm' diff --git a/app/components/ConnectDapp/MessageSuccess.js b/app/components/ConnectDapp/MessageSuccess.js deleted file mode 100644 index 8f86e2bee..000000000 --- a/app/components/ConnectDapp/MessageSuccess.js +++ /dev/null @@ -1,49 +0,0 @@ -// @flow - -import React from 'react' -import { useWalletConnect } from '../../context/WalletConnect/WalletConnectContext' -import { ROUTES } 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 CheckMarkIcon from '../../assets/icons/confirm-circle.svg' - -const MessageSuccess = () => { - const walletConnectCtx = useWalletConnect() - return ( - ( - { - walletConnectCtx.setMessageVerificationResult({}) - }} - /> - )} - renderHeaderIcon={() => ( -
- -
- )} - renderInstructions={false} - > -
- -

- {' '} - You have successfully{' '} - {walletConnectCtx.messageVerification.method === 'signMessage' - ? 'signed' - : 'verified'}{' '} - the message! -

-
-
-
-
- ) -} - -export default MessageSuccess diff --git a/app/components/ConnectDapp/TransactionSuccess.js b/app/components/ConnectDapp/TransactionSuccess.js deleted file mode 100644 index 0a0031a1b..000000000 --- a/app/components/ConnectDapp/TransactionSuccess.js +++ /dev/null @@ -1,51 +0,0 @@ -// @flow - -import React from 'react' -import { useWalletConnect } from '../../context/WalletConnect/WalletConnectContext' -import { ROUTES } 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 CheckMarkIcon from '../../assets/icons/confirm-circle.svg' - -const TransactionSuccess = () => { - const walletConnectCtx = useWalletConnect() - return ( - ( - { - walletConnectCtx.setTxHash('') - }} - /> - )} - renderHeaderIcon={() => ( -
- -
- )} - renderInstructions={false} - > -
- -

Transaction pending!

-

- Once your transaction has been confirmed it will appear in your - activity feed. -

-
-
-

- -
- {walletConnectCtx.txHash} -

-
-
- ) -} - -export default TransactionSuccess diff --git a/app/components/ConnectDapp/VerifyOrSignMessage.js b/app/components/ConnectDapp/VerifyOrSignMessage.js deleted file mode 100644 index a9041d20a..000000000 --- a/app/components/ConnectDapp/VerifyOrSignMessage.js +++ /dev/null @@ -1,186 +0,0 @@ -// @flow - -import React from 'react' -import classNames from 'classnames' -import { isEmpty } from 'lodash-es' -import { JsonRpcRequest } from '@json-rpc-tools/utils' - -import { useWalletConnect } from '../../context/WalletConnect/WalletConnectContext' -import { ROUTES } from '../../core/constants' -import CloseButton from '../CloseButton' -import FullHeightPanel from '../Panel/FullHeightPanel' -import WalletConnect 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' - -const VerifyOrSignMessage = ({ - request, - peer, - isHardwareLogin, - resetState, - history, - showSuccessNotification, - setLoading, - loading, - isSignMessage, -}: { - request: JsonRpcRequest, - peer: any, - isHardwareLogin: boolean, - resetState: () => any, - history: any, - showSuccessNotification: ({ message: string }) => any, - setLoading: boolean => any, - loading: boolean, - isSignMessage: boolean, -}) => { - const walletConnectCtx = useWalletConnect() - - const returnRequestParam = param => { - if (typeof param === 'string') return param - return '' - } - - return ( - ( - { - walletConnectCtx.rejectRequest(request) - resetState() - history.push(ROUTES.DASHBOARD) - }} - /> - )} - renderHeaderIcon={() => ( -
- -
- )} - renderInstructions={false} - > -
- -

{peer && peer.metadata.name} wants you to verify a message

- - {isHardwareLogin && ( - - } - renderText={() => ( -
- You can view the message below however, the N3 ledger app does - not currently support message signing/verification. -
- )} - className={styles.warningDialogue} - /> - )} - - {!isSignMessage && ( -
-
-
- {!isEmpty(request.params.request.params) && - Object.keys(request.params.request.params).map(param => ( -
-
- -
-
- {returnRequestParam( - request.params.request.params[param], - )} -
-
- ))} -
-
-
- )} - - {isSignMessage && ( -
-
-
- -
-
- {request.params.request.params.message} -
-
-
- )} - - {!isHardwareLogin && ( -
- Please confirm you would like to proceed -
- { - if (!loading) { - setLoading(true) - await walletConnectCtx.approveRequest(request) - setLoading(false) - } - }} - /> - - { - if (!loading) { - showSuccessNotification({ - message: `You have denied request from ${ - peer ? peer.metadata.name : 'unknown dApp' - }.`, - }) - walletConnectCtx.rejectRequest(request) - resetState() - history.push(ROUTES.DASHBOARD) - } - }} - /> -
-
- )} -
-
- ) -} - -export default VerifyOrSignMessage diff --git a/app/components/Contacts/ContactFormRefactor/ContactFormRefactor.jsx b/app/components/Contacts/ContactFormRefactor/ContactFormRefactor.jsx index 367bf2426..35b086d55 100644 --- a/app/components/Contacts/ContactFormRefactor/ContactFormRefactor.jsx +++ b/app/components/Contacts/ContactFormRefactor/ContactFormRefactor.jsx @@ -1,8 +1,8 @@ // @flow import React from 'react' import { intlShape } from 'react-intl' -import { wallet } from '@cityofzion/neon-js' -import { wallet as n3Wallet } from '@cityofzion/neon-js-next' +import { wallet } from '@cityofzion/neon-js-legacy' +import { wallet as n3Wallet } from '@cityofzion/neon-js' import { Box } from '@chakra-ui/react' import { BSNeo3 } from '@cityofzion/bs-neo3' diff --git a/app/components/CreateImportSplitWalletForm/CreateImportSplitWalletForm.jsx b/app/components/CreateImportSplitWalletForm/CreateImportSplitWalletForm.jsx index 773996d41..703442a98 100644 --- a/app/components/CreateImportSplitWalletForm/CreateImportSplitWalletForm.jsx +++ b/app/components/CreateImportSplitWalletForm/CreateImportSplitWalletForm.jsx @@ -1,6 +1,6 @@ // @flow import React, { Fragment } from 'react' -import { wallet } from '@cityofzion/neon-js' +import { wallet } from '@cityofzion/neon-js-legacy' import { withRouter } from 'react-router-dom' import { cloneDeep } from 'lodash-es' import { IntlShape, injectIntl, FormattedMessage } from 'react-intl' diff --git a/app/components/DapiStatus/DapiStatus.jsx b/app/components/DapiStatus/DapiStatus.jsx index 360596103..be216a588 100644 --- a/app/components/DapiStatus/DapiStatus.jsx +++ b/app/components/DapiStatus/DapiStatus.jsx @@ -3,19 +3,19 @@ import Tippy from '@tippyjs/react' import React from 'react' import { NavLink } from 'react-router-dom' +import { useWalletConnectWallet } from '@cityofzion/wallet-connect-sdk-wallet-react' import { ROUTES } from '../../core/constants' import Dapps from '../../assets/icons/dapps.svg' import GreenDapps from '../../assets/icons/green-dapps.svg' import Plus from '../../assets/icons/add.svg' import styles from './DapiStatus.scss' -import { useWalletConnect } from '../../context/WalletConnect/WalletConnectContext' const DapiStatus = ({ showSuccessNotification, }: { showSuccessNotification: ({ message: string }) => void, }) => { - const { sessions, disconnect } = useWalletConnect() + const { sessions, disconnect } = useWalletConnectWallet() return (
@@ -45,7 +45,7 @@ const DapiStatus = ({ s.peer.metadata.name }`, }) - disconnect(s.topic) + disconnect(s) }} > {' '} diff --git a/app/components/DappRequest/InvokeFunction/Invocation.jsx b/app/components/DappRequest/InvokeFunction/Invocation.jsx new file mode 100644 index 000000000..dc45f0319 --- /dev/null +++ b/app/components/DappRequest/InvokeFunction/Invocation.jsx @@ -0,0 +1,257 @@ +// @flow +import React, { useCallback, useEffect, useState } from 'react' +import classNames from 'classnames' +import { electron } from 'process' +import { rpc as n3RPC } from '@cityofzion/neon-js' +import styles from '../../../containers/ConnectDapp/styles.scss' +import DoraIcon from '../../../assets/icons/dora_icon_light.svg' +import DoraIconDark from '../../../assets/icons/dora_icon_dark.svg' +import Up from '../../../assets/icons/chevron-up.svg' +import WarningIcon from '../../../assets/icons/warning.svg' +import Down from '../../../assets/icons/chevron-down.svg' +import { getNode, getRPCEndpoint } from '../../../actions/nodeStorageActions' +import CopyToClipboard from '../../CopyToClipboard/CopyToClipboard' +import { + COLORS_BY_PARAMETER_TYPE, + WITNESS_SCOPE, +} from '../../../core/constants' +import Loader from '../../Loader/Loader' + +type Props = { + invocation: any, + requestParams: any, + theme: string, + net: string, +} + +const Invocation = ({ invocation, theme, net, requestParams }: Props) => { + const [isOpen, setIsOpen] = useState(true) + const [contractName, setContractName] = useState() + const [contractParameters, setContractParameter] = useState([]) + const [loading, setLoading] = useState(false) + + const handleOpenDoraLink = (contractHash: string) => { + electron.shell.openExternal( + net === 'MainNet' + ? `https://dora.coz.io/contract/neo3/mainnet/${contractHash}` + : `https://dora.coz.io/contract/neo3/testnet/${contractHash}`, + ) + } + + const handleContractManifest = useCallback( + async () => { + try { + setLoading(true) + let endpoint = await getNode(net) + if (!endpoint) { + endpoint = await getRPCEndpoint(net) + } + const rpcClient = new n3RPC.RPCClient(endpoint) + + const { + manifest: { name, abi }, + } = await rpcClient.getContractState(invocation.scriptHash) + + setContractName(name) + + const method = abi.methods.find( + method => method.name === invocation.operation, + ) + + const parameters = invocation.args.map((arg, i) => ({ + name: method?.parameters[i]?.name ?? i, + type: arg.type, + value: arg.value ?? 'null', + })) + + setContractParameter(parameters) + } finally { + setLoading(false) + } + }, + [net, invocation], + ) + + useEffect( + () => { + handleContractManifest() + }, + [handleContractManifest], + ) + + return loading ? ( +
+ +
+ ) : ( + <> +
+ {contractName ?? invocation.scriptHash} +
+ +
+
+ +
+ {invocation.scriptHash} + + {theme === 'Light' ? ( + handleOpenDoraLink(invocation.scriptHash)} + /> + ) : ( + handleOpenDoraLink(invocation.scriptHash)} + /> + )} +
+
+ +
+ +
{invocation.operation}
+
+ + {invocation.args?.length > 0 && ( +
+
setIsOpen(!isOpen)} + > + + +
{!isOpen ? : }
+
+ + {isOpen && ( +
+ {contractParameters.map((parameter, index) => ( +
+
+ {parameter.name} +
+
+ {JSON.stringify(parameter.value)} + +
+
+ {parameter.type} +
+
+ ))} +
+ )} +
+ )} + + {requestParams.signers?.length > 1 && ( +
+ + +
+ {requestParams.signers.map((signer, i) => ( +
+
+
{i}
+ + {WITNESS_SCOPE[String(signer.scopes)]} + {WITNESS_SCOPE[String(signer.scopes)] === 'Global' && ( + + )} +
+ +
+ +
{signer.account}
+
+ + {signer.allowedContracts?.length > 0 && + signer.allowedContracts.map(contract => ( +
+ +
+ {contract} + {theme === 'Light' ? ( + handleOpenDoraLink(contract)} + /> + ) : ( + handleOpenDoraLink(contract)} + /> + )} +
+
+ ))} +
+ ))} +
+
+ )} +
+
+ + ) +} + +export default Invocation diff --git a/app/components/DappRequest/InvokeFunction/InvokeFunction.jsx b/app/components/DappRequest/InvokeFunction/InvokeFunction.jsx new file mode 100644 index 000000000..923c390db --- /dev/null +++ b/app/components/DappRequest/InvokeFunction/InvokeFunction.jsx @@ -0,0 +1,266 @@ +/* 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-invoker' +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 { convertToArbitraryDecimals } from '../../../core/formatters' +import { getNode, getRPCEndpoint } from '../../../actions/nodeStorageActions' +import Invocation from './Invocation' +import InvokeResult from './InvokeResult' + +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 InvokeFunction = ({ + 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 [transactionHash, setTransactionHash] = useState() + const [errorMessage, setErrorMessage] = useState() + + const handleCalculateFee = useCallback( + async () => { + try { + setLoading(true) + 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) + + const extraNetworkFee = convertToArbitraryDecimals( + requestParams.extraNetworkFee ?? 0, + 8, + ) + const extraSystemFee = convertToArbitraryDecimals( + requestParams.extraSystemFee ?? 0, + 8, + ) + + setFee(total + Number(extraNetworkFee) + Number(extraSystemFee)) + } finally { + setLoading(false) + } + }, + [publicKey, net, requestParams], + ) + + 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) + + if (notificationId) { + hideNotification(notificationId) + } + + setTransactionHash(result) + } catch (error) { + setErrorMessage(error.message) + } finally { + setLoading(false) + } + } + + useEffect( + () => { + handleCalculateFee() + }, + [handleCalculateFee], + ) + + return loading ? ( + + ) : transactionHash || errorMessage ? ( + + ) : ( + ( + + )} + 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 InvokeFunction diff --git a/app/components/DappRequest/InvokeFunction/InvokeResult.jsx b/app/components/DappRequest/InvokeFunction/InvokeResult.jsx new file mode 100644 index 000000000..e472dd7d9 --- /dev/null +++ b/app/components/DappRequest/InvokeFunction/InvokeResult.jsx @@ -0,0 +1,60 @@ +/* eslint-disable no-nested-ternary */ +// @flow +import React from 'react' +import { ROUTES } 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 CheckMarkIcon from '../../../assets/icons/confirm-circle.svg' +import ErrorIcon from '../../../assets/icons/wc-error.svg' + +type Props = { + transactionHash?: string, + errorMessage?: string, +} + +const InvokeResult = ({ transactionHash, errorMessage }: Props) => ( + } + renderHeaderIcon={() => ( +
+ +
+ )} + renderInstructions={false} + > +
+ {transactionHash ? ( + <> + +

Transaction pending!

+

+ Once your transaction has been confirmed it will appear in your + activity feed. +

+
+
+

+ +
+ {transactionHash} +

+ + ) : errorMessage ? ( + <> + +

Transaction failed!

+

{errorMessage}

+
+
+ + ) : ( + <> + )} +
+
+) + +export default InvokeResult diff --git a/app/components/DappRequest/InvokeFunction/index.js b/app/components/DappRequest/InvokeFunction/index.js new file mode 100644 index 000000000..4348c017a --- /dev/null +++ b/app/components/DappRequest/InvokeFunction/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 InvokeFunction from './InvokeFunction' +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, +)(InvokeFunction) diff --git a/app/components/DappRequest/MessageSuccess.jsx b/app/components/DappRequest/MessageSuccess.jsx new file mode 100644 index 000000000..193be53dd --- /dev/null +++ b/app/components/DappRequest/MessageSuccess.jsx @@ -0,0 +1,38 @@ +// @flow +import React from 'react' +import { ROUTES } 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 CheckMarkIcon from '../../assets/icons/confirm-circle.svg' + +type Props = { + isVerify: boolean, +} + +const MessageSuccess = ({ isVerify }: Props) => ( + } + renderHeaderIcon={() => ( +
+ +
+ )} + renderInstructions={false} + > +
+ +

+ You have successfully + {!isVerify ? ' signed ' : ' verified '} + the message! +

+
+
+
+
+) + +export default MessageSuccess diff --git a/app/components/DappRequest/SignMessage/SignMessage.jsx b/app/components/DappRequest/SignMessage/SignMessage.jsx new file mode 100644 index 000000000..35c89fb25 --- /dev/null +++ b/app/components/DappRequest/SignMessage/SignMessage.jsx @@ -0,0 +1,142 @@ +// @flow +import React, { useState } from 'react' +import classNames from 'classnames' + +import { + useWalletConnectWallet, + TSessionRequest, + TSession, +} from '@cityofzion/wallet-connect-sdk-wallet-react' +import { ROUTES } from '../../../core/constants' +import CloseButton from '../../CloseButton' +import FullHeightPanel from '../../Panel/FullHeightPanel' +import WalletConnect 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 ConnectionLoader from '../../ConnectDapp/ConnectionLoader' +import MessageSuccess from '../MessageSuccess' + +type Props = { + request: TSessionRequest, + session: TSession, + isHardwareLogin: boolean, + history: any, + showSuccessNotification: ({ message: string }) => any, + showErrorNotification: ({ message: string }) => any, +} + +const VerifyMessage = ({ + request, + session, + isHardwareLogin, + history, + showSuccessNotification, + showErrorNotification, +}: Props) => { + const { approveRequest, rejectRequest } = useWalletConnectWallet() + + const [loading, setLoading] = useState(false) + const [success, setSuccess] = useState(false) + + 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) + await approveRequest(request) + setSuccess(true) + } catch (error) { + showErrorNotification({ message: error.message }) + history.push(ROUTES.DASHBOARD) + } finally { + setLoading(false) + } + } + + if (loading) return + + return success ? ( + + ) : ( + ( + + )} + renderHeaderIcon={() => ( +
+ +
+ )} + renderInstructions={false} + > +
+ +

{session.peer.metadata.name} wants you to verify a message

+ + {isHardwareLogin && ( + + } + renderText={() => ( +
+ You can view the message below however, the N3 ledger app does + not currently support message signing/verification. +
+ )} + className={styles.warningDialogue} + /> + )} + +
+
+
+ +
+
+ {request.params.request.params.message} +
+
+
+ + {!isHardwareLogin && ( +
+ Please confirm you would like to proceed +
+ + + +
+
+ )} +
+
+ ) +} + +export default VerifyMessage diff --git a/app/components/DappRequest/SignMessage/index.js b/app/components/DappRequest/SignMessage/index.js new file mode 100644 index 000000000..ef33fa544 --- /dev/null +++ b/app/components/DappRequest/SignMessage/index.js @@ -0,0 +1,28 @@ +import { withRouter } from 'react-router-dom' +import { compose } from 'recompose' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +import SignMessage from './SignMessage' +import { + showSuccessNotification, + showErrorNotification, +} from '../../../modules/notifications' +import withAuthData from '../../../hocs/withAuthData' + +const actionCreators = { + showSuccessNotification, + showErrorNotification, +} + +const mapDispatchToProps = dispatch => + bindActionCreators(actionCreators, dispatch) + +export default compose( + connect( + null, + mapDispatchToProps, + ), + withAuthData(), + withRouter, +)(SignMessage) diff --git a/app/components/DappRequest/VerifyMessage/VerifyMessage.jsx b/app/components/DappRequest/VerifyMessage/VerifyMessage.jsx new file mode 100644 index 000000000..31dab516d --- /dev/null +++ b/app/components/DappRequest/VerifyMessage/VerifyMessage.jsx @@ -0,0 +1,160 @@ +// @flow +import React, { useState } from 'react' +import classNames from 'classnames' +import { isEmpty } from 'lodash-es' + +import { + useWalletConnectWallet, + TSessionRequest, + TSession, +} from '@cityofzion/wallet-connect-sdk-wallet-react' +import { ROUTES } from '../../../core/constants' +import CloseButton from '../../CloseButton' +import FullHeightPanel from '../../Panel/FullHeightPanel' +import WalletConnect 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 ConnectionLoader from '../../ConnectDapp/ConnectionLoader' +import MessageSuccess from '../MessageSuccess' + +type Props = { + request: TSessionRequest, + session: TSession, + isHardwareLogin: boolean, + history: any, + showSuccessNotification: ({ message: string }) => any, + showErrorNotification: ({ message: string }) => any, +} + +const VerifyMessage = ({ + request, + session, + isHardwareLogin, + history, + showSuccessNotification, + showErrorNotification, +}: Props) => { + const { approveRequest, rejectRequest } = useWalletConnectWallet() + + const [loading, setLoading] = useState(false) + const [success, setSuccess] = useState(false) + + 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) + await approveRequest(request) + setSuccess(true) + } catch (error) { + showErrorNotification({ message: error.message }) + history.push(ROUTES.DASHBOARD) + } finally { + setLoading(false) + } + } + + if (loading) return + + return success ? ( + + ) : ( + ( + + )} + renderHeaderIcon={() => ( +
+ +
+ )} + renderInstructions={false} + > +
+ +

{session.peer.metadata.name} wants you to verify a message

+ + {isHardwareLogin && ( + + } + renderText={() => ( +
+ You can view the message below however, the N3 ledger app does + not currently support message signing/verification. +
+ )} + className={styles.warningDialogue} + /> + )} + +
+
+
+ {!isEmpty(request.params.request.params) && + Object.entries(request.params.request.params).map( + ([key, value]) => ( +
+
+ +
+
+ {JSON.stringify(value)} +
+
+ ), + )} +
+
+
+ + {!isHardwareLogin && ( +
+ Please confirm you would like to proceed +
+ + + +
+
+ )} +
+
+ ) +} + +export default VerifyMessage diff --git a/app/components/DappRequest/VerifyMessage/index.js b/app/components/DappRequest/VerifyMessage/index.js new file mode 100644 index 000000000..2020af3c3 --- /dev/null +++ b/app/components/DappRequest/VerifyMessage/index.js @@ -0,0 +1,28 @@ +import { withRouter } from 'react-router-dom' +import { compose } from 'recompose' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +import VerifyMessage from './VerifyMessage' +import { + showSuccessNotification, + showErrorNotification, +} from '../../../modules/notifications' +import withAuthData from '../../../hocs/withAuthData' + +const actionCreators = { + showSuccessNotification, + showErrorNotification, +} + +const mapDispatchToProps = dispatch => + bindActionCreators(actionCreators, dispatch) + +export default compose( + connect( + null, + mapDispatchToProps, + ), + withAuthData(), + withRouter, +)(VerifyMessage) diff --git a/app/components/Migration/CreateLedgerMigrationWallet/CreateLedgerMigrationWallet.jsx b/app/components/Migration/CreateLedgerMigrationWallet/CreateLedgerMigrationWallet.jsx index d1460c189..f30af3888 100644 --- a/app/components/Migration/CreateLedgerMigrationWallet/CreateLedgerMigrationWallet.jsx +++ b/app/components/Migration/CreateLedgerMigrationWallet/CreateLedgerMigrationWallet.jsx @@ -1,6 +1,6 @@ // @flow import React from 'react' -import { wallet as n3Wallet } from '@cityofzion/neon-js-next' +import { wallet as n3Wallet } from '@cityofzion/neon-js' import LoginLedgerNanoS from '../../../containers/LoginLedgerNanoS' import n3Logo from '../../../assets/images/n3_logo.png' diff --git a/app/components/Migration/CreateMigrationWallet/CreateMigrationWallet.jsx b/app/components/Migration/CreateMigrationWallet/CreateMigrationWallet.jsx index 90ac60269..32b90bb88 100644 --- a/app/components/Migration/CreateMigrationWallet/CreateMigrationWallet.jsx +++ b/app/components/Migration/CreateMigrationWallet/CreateMigrationWallet.jsx @@ -2,7 +2,7 @@ import classNames from 'classnames' import React from 'react' import { intlShape } from 'react-intl' -import { wallet as n3Wallet } from '@cityofzion/neon-js-next' +import { wallet as n3Wallet } from '@cityofzion/neon-js' import n3Logo from '../../../assets/images/n3_logo.png' import Address from '../../Blockchain/Address/Address' diff --git a/app/components/Modals/CustomNetworkModal/CustomNetworkModal.jsx b/app/components/Modals/CustomNetworkModal/CustomNetworkModal.jsx index f0085432b..e3f09e5a1 100644 --- a/app/components/Modals/CustomNetworkModal/CustomNetworkModal.jsx +++ b/app/components/Modals/CustomNetworkModal/CustomNetworkModal.jsx @@ -1,7 +1,13 @@ // @flow import React, { useEffect } from 'react' import { Box } from '@chakra-ui/react' -import { api, u, rpc as neonJsRpc, sc, wallet } from '@cityofzion/neon-js' +import { + api, + u, + rpc as neonJsRpc, + sc, + wallet, +} from '@cityofzion/neon-js-legacy' import FullHeightPanel from '../../Panel/FullHeightPanel' import BaseModal from '../BaseModal' diff --git a/app/components/Modals/ImportTransactionModal/ImportTransactionModal.jsx b/app/components/Modals/ImportTransactionModal/ImportTransactionModal.jsx index 281e142c8..a560816db 100644 --- a/app/components/Modals/ImportTransactionModal/ImportTransactionModal.jsx +++ b/app/components/Modals/ImportTransactionModal/ImportTransactionModal.jsx @@ -1,7 +1,7 @@ // @flow import React, { Fragment } from 'react' import moment from 'moment' -import { tx, rpc, api } from '@cityofzion/neon-js' +import { tx, rpc, api } from '@cityofzion/neon-js-legacy' import { Tab, Tabs, TabList, TabPanel } from 'react-tabs' import classNames from 'classnames' import { isEmpty } from 'lodash-es' diff --git a/app/components/Modals/NetworkSwitchModal/NetworkSwitchModal.jsx b/app/components/Modals/NetworkSwitchModal/NetworkSwitchModal.jsx index 26ff6850d..f06836200 100644 --- a/app/components/Modals/NetworkSwitchModal/NetworkSwitchModal.jsx +++ b/app/components/Modals/NetworkSwitchModal/NetworkSwitchModal.jsx @@ -8,38 +8,64 @@ import CloseButton from '../../CloseButton' import Cog from '../../../assets/icons/cog-icon.svg' import Button from '../../Button' +import { networksIDSByNetworksLabel } from '../../../util/walletConnect' +import { useSettingsContext } from '../../../context/settings/SettingsContext' type Props = { hideModal: Function, net: string, dAppName: string, - approveSession: (network: string) => void, - rejectSession: () => void, + onSwitch: () => void, + onCancel: (error?: string) => void, switchNetworks: (id: string) => void, + saveSelectedNode: Function, + proposalNetwork: string, } -export default function NetworkSwitchModal(props: Props) { - const { - hideModal, - net, - dAppName = 'The dApp you are using', - approveSession, - rejectSession, - switchNetworks, - } = props - - const requestedNetwork = net === 'MainNet' ? 'TestNet' : 'MainNet' - const requestedNetworkId = net === 'MainNet' ? '2' : '1' +export default function NetworkSwitchModal({ + hideModal, + net, + dAppName, + onSwitch, + onCancel, + switchNetworks, + saveSelectedNode, + proposalNetwork, +}: Props) { + const { settings } = useSettingsContext() function handleCancelClick() { - rejectSession() + onCancel() hideModal() } function handleApproveClick() { - switchNetworks(requestedNetworkId) - approveSession(requestedNetwork.toLowerCase()) - hideModal() + try { + const network = Object.entries(networksIDSByNetworksLabel).find( + ([key]) => key === proposalNetwork, + ) + + if (!network) throw new Error('Invalid network') + const networkId = (network[1]: any) + + if (networkId === 'Custom') { + const customNetwork = settings.customNetworks[0] + + if (!customNetwork) throw new Error('There is no custom network') + saveSelectedNode({ + url: customNetwork.rpc, + net: 'Custom', + label: customNetwork.label, + }) + } + + switchNetworks(networkId) + onSwitch() + } catch (error) { + onCancel(error.message) + } finally { + hideModal() + } } return ( @@ -67,8 +93,7 @@ export default function NetworkSwitchModal(props: Props) { )} >

- {' '} - {dAppName} has made a session request for {requestedNetwork} however + {dAppName} has made a session request for {proposalNetwork} however you are currently on {net}, would you like to change networks and accept this request?

diff --git a/app/components/Modals/NetworkSwitchModal/index.js b/app/components/Modals/NetworkSwitchModal/index.js index bc4710003..678b4ed40 100644 --- a/app/components/Modals/NetworkSwitchModal/index.js +++ b/app/components/Modals/NetworkSwitchModal/index.js @@ -1,54 +1,24 @@ // @flow import { compose } from 'recompose' -import { injectIntl } from 'react-intl' -import { bindActionCreators } from 'redux' -import { connect } from 'react-redux' import { withActions, type Actions } from 'spunky' -import Modal from './NetworkSwitchModal' +import NetworkSwitchModal from './NetworkSwitchModal' import withNetworkData from '../../../hocs/withNetworkData' import withAuthData from '../../../hocs/withAuthData' import networkActions from '../../../actions/networkActions' -import { - showErrorNotification, - showSuccessNotification, - showInfoNotification, - hideNotification, -} from '../../../modules/notifications' -import accountActions from '../../../actions/accountActions' - -const actionCreators = { - showErrorNotification, - showSuccessNotification, - showInfoNotification, - hideNotification, -} +import nodeStorageActions from '../../../actions/nodeStorageActions' const mapActionsToProps = (actions: Actions): Object => ({ switchNetworks: networkId => actions.call({ networkId }), }) -const mapAccountActionsToProps = (actions, props) => ({ - loadWalletData: (net: string) => - actions.call({ - net, - address: props.address, - tokens: props.tokens, - chain: props.chain, - }), +const mapSaveNodeActionsToProps = actions => ({ + saveSelectedNode: ({ url, net, label }) => actions.call({ url, net, label }), }) -const mapDispatchToProps = dispatch => - bindActionCreators(actionCreators, dispatch) - export default compose( - connect( - null, - mapDispatchToProps, - ), - injectIntl, withNetworkData(), withAuthData(), withActions(networkActions, mapActionsToProps), - withActions(accountActions, mapAccountActionsToProps), -)(Modal) + withActions(nodeStorageActions, mapSaveNodeActionsToProps), +)(NetworkSwitchModal) diff --git a/app/components/Modals/TransferNftModal/TransferNftModal.jsx b/app/components/Modals/TransferNftModal/TransferNftModal.jsx index bbc8116b3..ec5717c03 100644 --- a/app/components/Modals/TransferNftModal/TransferNftModal.jsx +++ b/app/components/Modals/TransferNftModal/TransferNftModal.jsx @@ -1,9 +1,10 @@ // @flow import React, { useState, useEffect } from 'react' import { FormattedMessage } from 'react-intl' -import { wallet as n3Wallet } from '@cityofzion/neon-js-next' +import { wallet as n3Wallet } from '@cityofzion/neon-js' import { BSNeo3 } from '@cityofzion/bs-neo3' +import { NeonInvoker } from '@cityofzion/neon-invoker' import { NFT } from '../../../containers/NftGallery/NftGallery' import Button from '../../Button' import SelectInput from '../../Inputs/SelectInput' @@ -12,8 +13,6 @@ import N3Fees from '../../Send/N3Fees' import BaseModal from '../BaseModal' import styles from './TransferNftModal.scss' import { getNode, getRPCEndpoint } from '../../../actions/nodeStorageActions' -import N3Helper from '../../../context/WalletConnect/helpers' -import { convertToArbitraryDecimals } from '../../../core/formatters' import { addPendingTransaction } from '../../../actions/pendingTransactionActions' import { useContactsContext } from '../../../context/contacts/ContactsContext' import { MODAL_TYPES } from '../../../core/constants' @@ -31,10 +30,10 @@ type Props = { address: string, tokenId: string, wif: string, - showSuccessNotification: ({ message: string }) => any, - showErrorNotification: ({ message: string }) => any, - showInfoNotification: ({ message: string }) => any, - hideNotification: (id: string) => void, + showSuccessNotification({ message: string }): any, + showErrorNotification({ message: string }): any, + showInfoNotification({ message: string, autoDismiss: number }): any, + hideNotification(id: string): any, dispatch: any => any, isHardwareLogin: boolean, signingFunction: () => void, @@ -52,14 +51,14 @@ export default function TransferNftModal(props: Props) { address, wif, dispatch, - isHardwareLogin, - signingFunction, showSuccessNotification, showErrorNotification, showInfoNotification, hideNotification, recipientAddressProp, publicKey, + isHardwareLogin, + signingFunction, } = props function handleSubmit() {} @@ -72,8 +71,7 @@ export default function TransferNftModal(props: Props) { recipientAddressProp ?? '', ) const [recipientAddressError, setRecipientAddressError] = useState('') - const [gasFee, setGasFee] = useState(DEFAULT_FEES) - const [feesInitialized, setFeesInitialized] = useState(false) + const [fees, setFees] = useState(DEFAULT_FEES) const [sendButtonDisabled, setSendButtonDisabled] = useState(false) const [loading, setLoading] = useState(true) const { contacts } = useContactsContext() @@ -160,53 +158,54 @@ export default function TransferNftModal(props: Props) { async function transfer() { try { setLoading(true) - let endpoint = await getNode(net) - if (!endpoint) { - endpoint = await getRPCEndpoint(net) + let rpcAddress = await getNode(net) + if (!rpcAddress) { + rpcAddress = await getRPCEndpoint(net) } const account = new n3Wallet.Account(isHardwareLogin ? publicKey : wif) - const testReq = { - params: { - request: { - method: 'multiInvoke', - params: { - invocations: [ - { - scriptHash: contract, - operation: 'transfer', - args: [ - { - type: 'Hash160', - value: recipientAddress, - }, - { type: 'ByteArray', value: tokenId }, - { type: 'Any', value: null }, - ], - }, - ], - signers: [{ scopes: 1 }], - }, - }, - }, - } - const results = await new N3Helper(endpoint, 0).rpcCall( + const invoker = await NeonInvoker.init({ + rpcAddress, account, - testReq, - isHardwareLogin, - signingFunction, - showInfoNotification, - hideNotification, - ) + signingCallback: signingFunction, + }) + + let notificationId + + if (isHardwareLogin) { + notificationId = showInfoNotification({ + message: 'Please sign the transaction on your hardware device', + autoDismiss: 0, + }) + } + + const hash = await invoker.invokeFunction({ + invocations: [ + { + scriptHash: contract, + operation: 'transfer', + args: [ + { + type: 'Hash160', + value: recipientAddress, + }, + { type: 'ByteArray', value: tokenId }, + { type: 'Any', value: null }, + ], + }, + ], + }) - const { result } = results + if (notificationId) { + hideNotification(notificationId) + } dispatch( addPendingTransaction.call({ address, net, tx: { - hash: result, + hash, sendEntries: [ { amount: 1, address, contractHash: contract, symbol: 'N/A' }, ], @@ -217,67 +216,52 @@ export default function TransferNftModal(props: Props) { showSuccessNotification({ message: 'Transaction pending! Your NFT will be transferred shortly.', }) - setLoading(false) - hideModal() } catch (e) { - hideModal() showErrorNotification({ message: e.message, }) + } finally { setLoading(false) + hideModal() } } - useEffect(() => { - async function testInvoke() { - setLoading(true) - let endpoint = await getNode(net) - if (!endpoint) { - endpoint = await getRPCEndpoint(net) - } - const account = new n3Wallet.Account(address) - const testReq = { - params: { - request: { - method: 'testInvoke', - params: { - invocations: [ + useEffect( + () => { + ;(async () => { + let rpcAddress = await getNode(net) + if (!rpcAddress) { + rpcAddress = await getRPCEndpoint(net) + } + const account = new n3Wallet.Account(isHardwareLogin ? publicKey : wif) + + const invoker = await NeonInvoker.init({ rpcAddress, account }) + const { networkFee, systemFee } = await invoker.calculateFee({ + invocations: [ + { + scriptHash: contract, + operation: 'transfer', + args: [ { - scriptHash: contract, - operation: 'transfer', - args: [ - { - type: 'Hash160', - value: address, - }, - { type: 'ByteArray', value: tokenId }, - { type: 'Any', value: null }, - ], + type: 'Hash160', + value: address, }, + { type: 'ByteArray', value: tokenId }, + { type: 'Any', value: null }, ], - signers: [{ scopes: 1 }], }, - }, - }, - } - const results = await new N3Helper(endpoint, 0).rpcCall( - account, - testReq, - isHardwareLogin, - signingFunction, - showInfoNotification, - hideNotification, - ) - const fee = convertToArbitraryDecimals(results.result.gasconsumed) - setGasFee({ - networkFee: fee, - systemFee: 0, - }) - setFeesInitialized(true) - setLoading(false) - } - testInvoke() - }, []) + ], + signers: [{ scopes: 1 }], + }) + + setFees({ + networkFee, + systemFee, + }) + })() + }, + [address, tokenId, isHardwareLogin, publicKey, wif, net, contract], + ) function createContactList(): Array { const filteredContacts = Object.keys(contacts).filter(contact => @@ -314,10 +298,7 @@ export default function TransferNftModal(props: Props) { error={recipientAddressError} /> - +