diff --git a/.env.example b/.env.example index 9f0b5fafd..43a898cef 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ # MANIFEST_TYPE values: MV2 or MV3 -MANIFEST_TYPE=MV3 \ No newline at end of file +MANIFEST_TYPE=MV3 +NODE_ENV=development \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4e8350077..1c08e15e3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '16' + node-version: '18' check-latest: true - run: yarn install - run: yarn test-all @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '16' + node-version: '18' check-latest: true - run: yarn install - run: | diff --git a/package.json b/package.json index 9aefe96a7..00fdd0a57 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,11 @@ }, "scripts": { "generate-manifest": "node ./source/config/generateManifest.js", - "dev:chrome": "yarn generate-manifest && jq 'del(.environment) | .permissions = if .permissions then (if (.permissions | index(\"webRequest\")) == null then .permissions + [\"webRequest\"] else .permissions end) else [\"webRequest\"] end' manifest.json > modified_manifest.json && mv modified_manifest.json manifest.json && cross-env NODE_ENV=development cross-env TARGET_BROWSER=chrome webpack --watch", + "dev:chrome": "yarn generate-manifest && cross-env NODE_ENV=development cross-env TARGET_BROWSER=chrome webpack --config source/config/webpack.dev.js --watch", + "build:chrome": "yarn generate-manifest && cross-env NODE_ENV=production cross-env TARGET_BROWSER=chrome webpack --config source/config/webpack.prod.js", "dev-watch-requests:chrome": "jq '.permissions = if .permissions then (if (.permissions | index(\"webRequest\")) == null then .permissions + [\"webRequest\"] else .permissions end) else [\"webRequest\"] end | .environment = {\"WATCH_REQUESTS\": \"active\"}' manifest.json > modified_manifest.json && mv modified_manifest.json manifest.json && cross-env NODE_ENV=development cross-env TARGET_BROWSER=chrome webpack --watch", "dev:firefox": "jq '.permissions = if .permissions then (if (.permissions | index(\"webRequest\")) == null then .permissions + [\"webRequest\"] else .permissions end) else [\"webRequest\"] end' manifest.json > modified_manifest.json && mv modified_manifest.json manifest.json && cross-env NODE_ENV=development cross-env TARGET_BROWSER=firefox webpack --watch", "dev:opera": "jq '.permissions = if .permissions then (if (.permissions | index(\"webRequest\")) == null then .permissions + [\"webRequest\"] else .permissions end) else [\"webRequest\"] end' manifest.json > modified_manifest.json && mv modified_manifest.json manifest.json && cross-env NODE_ENV=development cross-env TARGET_BROWSER=opera webpack --watch", - "build:chrome": "jq '.permissions = if .permissions then (.permissions | map(select(. != \"webRequest\"))) else .permissions end' manifest.json > modified_manifest.json && mv modified_manifest.json manifest.json && cross-env NODE_ENV=production cross-env TARGET_BROWSER=chrome webpack", "build:firefox": "jq '.permissions = if .permissions then (.permissions | map(select(. != \"webRequest\"))) else .permissions end' manifest.json > modified_manifest.json && mv modified_manifest.json manifest.json && cross-env NODE_ENV=production cross-env TARGET_BROWSER=firefox webpack", "build:opera": "jq '.permissions = if .permissions then (.permissions | map(select(. != \"webRequest\"))) else .permissions end' manifest.json > modified_manifest.json && mv modified_manifest.json manifest.json && cross-env NODE_ENV=production cross-env TARGET_BROWSER=opera webpack", "build": "yarn run build:chrome && yarn run build:firefox && yarn run build:opera", @@ -170,10 +170,12 @@ "ts-jest": "^27.1.3", "typescript": "4.1.5", "webpack": "^5.82.0", + "webpack-bundle-analyzer": "^4.10.2", "webpack-cli": "^5.0.2", "webpack-dev-server": "^4.13.3", + "webpack-merge": "^5.10.0", "wext-manifest-loader": "^3.0.0", - "wext-manifest-webpack-plugin": "^1.2.1" + "wext-manifest-webpack-plugin": "^1.4.1" }, "main": "index.js", "bugs": { diff --git a/patches/webextension-polyfill+0.8.0.patch b/patches/webextension-polyfill+0.8.0.patch deleted file mode 100644 index a967b8215..000000000 --- a/patches/webextension-polyfill+0.8.0.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/node_modules/webextension-polyfill/dist/browser-polyfill.js b/node_modules/webextension-polyfill/dist/browser-polyfill.js -index 230b763..68170c4 100644 ---- a/node_modules/webextension-polyfill/dist/browser-polyfill.js -+++ b/node_modules/webextension-polyfill/dist/browser-polyfill.js -@@ -24,6 +24,7 @@ - - if (typeof browser === "undefined" || Object.getPrototypeOf(browser) !== Object.prototype) { - const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received."; -+ const CHROME_SEND_MESSAGE_CALLBACK_NO_LISTENER_MESSAGE = "A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received"; // No period - const SEND_RESPONSE_DEPRECATION_WARNING = "Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)"; // Wrapping the bulk of this polyfill in a one-time-use function is a minor - // optimization for Firefox. Since Spidermonkey does not fully parse the - // contents of a function until the first time it's called, and since it will -@@ -1181,7 +1182,7 @@ - // Detect when none of the listeners replied to the sendMessage call and resolve - // the promise to undefined as in Firefox. - // See https://github.com/mozilla/webextension-polyfill/issues/130 -- if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE) { -+ if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE || extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_LISTENER_MESSAGE) { - resolve(); - } else { - reject(new Error(extensionAPIs.runtime.lastError.message)); diff --git a/patches/webextension-polyfill-ts++webextension-polyfill+0.7.0.patch b/patches/webextension-polyfill-ts++webextension-polyfill+0.7.0.patch deleted file mode 100644 index 37b569812..000000000 --- a/patches/webextension-polyfill-ts++webextension-polyfill+0.7.0.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/node_modules/webextension-polyfill-ts/node_modules/webextension-polyfill/dist/browser-polyfill.js b/node_modules/webextension-polyfill-ts/node_modules/webextension-polyfill/dist/browser-polyfill.js -index 51f7fb6..eae5bce 100644 ---- a/node_modules/webextension-polyfill-ts/node_modules/webextension-polyfill/dist/browser-polyfill.js -+++ b/node_modules/webextension-polyfill-ts/node_modules/webextension-polyfill/dist/browser-polyfill.js -@@ -24,6 +24,7 @@ - - if (typeof browser === "undefined" || Object.getPrototypeOf(browser) !== Object.prototype) { - const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received."; -+ const CHROME_SEND_MESSAGE_CALLBACK_NO_LISTENER_MESSAGE = "A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received"; // No period - const SEND_RESPONSE_DEPRECATION_WARNING = "Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)"; // Wrapping the bulk of this polyfill in a one-time-use function is a minor - // optimization for Firefox. Since Spidermonkey does not fully parse the - // contents of a function until the first time it's called, and since it will -@@ -1147,7 +1148,7 @@ - // Detect when none of the listeners replied to the sendMessage call and resolve - // the promise to undefined as in Firefox. - // See https://github.com/mozilla/webextension-polyfill/issues/130 -- if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE) { -+ if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE || extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_LISTENER_MESSAGE) { - resolve(); - } else { - reject(extensionAPIs.runtime.lastError); diff --git a/patches/wext-manifest-webpack-plugin+1.2.1.patch b/patches/wext-manifest-webpack-plugin+1.2.1.patch deleted file mode 100644 index e43ea43b6..000000000 --- a/patches/wext-manifest-webpack-plugin+1.2.1.patch +++ /dev/null @@ -1,139 +0,0 @@ -diff --git a/node_modules/wext-manifest-webpack-plugin/lib/plugin.js b/node_modules/wext-manifest-webpack-plugin/lib/plugin.js -index d52d6cd..1be21ae 100644 ---- a/node_modules/wext-manifest-webpack-plugin/lib/plugin.js -+++ b/node_modules/wext-manifest-webpack-plugin/lib/plugin.js -@@ -8,18 +8,34 @@ exports.WextManifestWebpackPlugin = void 0; - * @author abhijithvijayan - * @license MIT License - */ -+ -+const defaultOptions = { -+ extensions: ["css", "scss", "sass", "less", "styl"], -+ scriptExtensions: ["js", "mjs"], -+ ignore: undefined, -+}; -+ - require("emoji-log"); - const PLUGIN_NAME = 'wext-manifest-webpack-plugin'; --function getEntryResource(module) { -- const resource = null; -- if (module && typeof module.resource === 'string') { -- return module.resource; -- } -- return resource; --} -+ - class WextManifestWebpackPlugin { -+ constructor(options) { -+ this.apply = this.apply.bind(this); -+ this.options = Object.assign({}, defaultOptions, options); -+ } - // Define `apply` as its prototype method which is supplied with compiler as its argument - apply(compiler) { -+ const extensionsWithoutDots = this.options.extensions.map(e => -+ e[0] === "." ? e.substring(1) : e -+ ); -+ -+ const patternOneOfExtensions = extensionsWithoutDots -+ .map(ext => escapeRegExp(ext)) -+ .join("|"); -+ -+ const resourcesRegex = new RegExp( -+ `[.](${patternOneOfExtensions})([?].*)?$` -+ ); - /** - * webpack 4+ comes with a new plugin system. - * -@@ -30,19 +46,33 @@ class WextManifestWebpackPlugin { - if (hooks) { - // Runs plugin after a compilation has been created. - hooks.compilation.tap(PLUGIN_NAME, (compilation) => { -+ const resourcesCache = []; - // Triggered when an asset from a chunk was added to the compilation. - compilation.hooks.chunkAsset.tap(PLUGIN_NAME, (chunk, file) => { -+ - // Only handle js files with entry modules -- if (!file.endsWith('.js') || !chunk.hasEntryModule()) { -- return; -- } -+ let isNotScript = defaultOptions.scriptExtensions.every((ext) => file.lastIndexOf('.' + ext) < 0) -+ if(isNotScript) return -+ -+ // has entry modules -+ if (compilation.chunkGraph.getNumberOfEntryModules(chunk) < 1) return; -+ const entryModules = Array.from(compilation.chunkGraph.getChunkEntryModulesIterable(chunk)); -+ if (entryModules.length < 1) return; -+ -+ const entryModule = entryModules[0]; -+ const rawResources = collectEntryResources(compilation, entryModule, resourcesCache); -+ - // Returns path containing name of asset -- const resource = getEntryResource(chunk.entryModule); -- const isManifest = (resource && /manifest\.json$/.test(resource)) || false; -+ const resources = defaultOptions.ignore ? -+ rawResources.filter(r => !r.match(this.options.ignore)) -+ : rawResources; -+ -+ const isManifest = resources.length && -+ resources.every(resource => resourcesRegex.test(resource) || /manifest\.json$/.test(resource)); -+ - if (isManifest) { -- chunk.files = chunk.files.filter((f) => { -- return f !== file; -- }); -+ chunk.files.delete(file) -+ - delete compilation.assets[file]; - // https://github.com/abhijithvijayan/wext-manifest-webpack-plugin/issues/1 - // console.emoji('🦄', `${PLUGIN_NAME}: removed ${file}`, 29); -@@ -53,4 +83,49 @@ class WextManifestWebpackPlugin { - } - } - } -+function collectEntryResources(compilation, module, cache) { -+ const index = compilation.moduleGraph.getPreOrderIndex(module); -+ -+ // index of module is unique per compilation -+ // module.id can be null, not used here -+ if (cache[index] !== undefined) { -+ return cache[index]; -+ } -+ -+ if (typeof module.resource == "string") { -+ const resources = [module.resource]; -+ cache[index] = resources; -+ return resources; -+ } -+ -+ const resources = []; -+ if (module.dependencies) { -+ const hasModuleGraphSupport = compilation.hasOwnProperty('moduleGraph'); -+ module.dependencies.forEach(dep => { -+ if(dep) { -+ const module = hasModuleGraphSupport ? compilation.moduleGraph.getModule(dep) : dep.module; -+ const originModule = hasModuleGraphSupport ? compilation.moduleGraph.getParentModule(dep) : dep.originModule; -+ const nextModule = module || originModule; -+ if (nextModule) { -+ const depResources = collectEntryResources(compilation, nextModule, cache); -+ for (let index = 0, length = depResources.length; index !== length; index++) { -+ resources.push(depResources[index]); -+ } -+ } -+ } -+ }); -+ } -+ -+ cache[index] = resources; -+ return resources; -+} -+ -+const reRegExpChar = /[\\^$.*+?()[\]{}|]/g; -+const reHasRegExpChar = RegExp(reRegExpChar.source); -+function escapeRegExp(string) { -+ string = String(string); -+ return string && reHasRegExpChar.test(string) -+ ? string.replace(reRegExpChar, "\\$&") -+ : string; -+} - exports.WextManifestWebpackPlugin = WextManifestWebpackPlugin; -\ No newline at end of file diff --git a/source/components/Header/AccountHeader.tsx b/source/components/Header/AccountHeader.tsx index 05c9a2a9a..d279a500f 100644 --- a/source/components/Header/AccountHeader.tsx +++ b/source/components/Header/AccountHeader.tsx @@ -21,7 +21,7 @@ import { DefaultModal, } from 'components/index'; import { useUtils } from 'hooks/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { RootState } from 'state/store'; import { ellipsis } from 'utils/index'; @@ -288,7 +288,7 @@ const RenderAccountsListByBitcoinBased = ( export const AccountMenu: React.FC = () => { const { navigate } = useUtils(); - const { wallet, dapp } = getController(); + const { controllerEmitter } = useController(); const isBitcoinBased = useSelector( (state: RootState) => state.vault.isBitcoinBased ); @@ -301,11 +301,20 @@ export const AccountMenu: React.FC = () => { currentWindow: true, }); const host = new URL(tabs[0].url).hostname; - const connectedAccount = dapp.getAccount(host); - wallet.setAccount(Number(id), type, host, connectedAccount); + + await controllerEmitter(['dapp', 'getAccount'], [host]).then( + async (res) => { + await controllerEmitter( + ['wallet', 'setAccount'], + [Number(id), type, host, res] + ); + } + ); + return; } - wallet.setAccount(Number(id), type); + + await controllerEmitter(['wallet', 'setAccount'], [Number(id), type]); }; const cursorType = isBitcoinBased ? 'cursor-not-allowed' : 'cursor-pointer'; @@ -409,7 +418,7 @@ export const AccountHeader: React.FC = () => { const [isLoading, setIsLoading] = useState(false); const [isOpenModal, setIsOpenModal] = useState(false); const [isReconectModalOpen, setIsReconectModalOpen] = useState(false); - const controller = getController(); + const { controllerEmitter } = useController(); const isLedger = activeAccount.type === KeyringAccountType.Ledger; const url = chrome.runtime.getURL('app.html'); @@ -443,25 +452,35 @@ export const AccountHeader: React.FC = () => { const handleVerifyAddress = async () => { try { setIsLoading(true); - await controller.wallet.ledgerSigner.utxo.verifyUtxoAddress( - activeAccount.id + + await controllerEmitter( + ['wallet', 'ledgerSigner', 'utxo', 'verifyUtxoAddress'], + [activeAccount.id] ); + setIsLoading(false); + setIsOpenModal(false); + alert.success(t('home.addressVerified')); } catch (error) { const isNecessaryReconnect = error.message.includes( 'read properties of undefined' ); + if (isNecessaryReconnect) { setIsReconectModalOpen(true); return; } + const wasDeniedByUser = error?.message?.includes('denied by the user'); + if (wasDeniedByUser) { alert.error(t('home.verificationDeniedByUser')); } + setIsOpenModal(false); + setIsLoading(false); } }; diff --git a/source/components/Header/Header.tsx b/source/components/Header/Header.tsx index 4d0a29f20..4b7ec4c33 100755 --- a/source/components/Header/Header.tsx +++ b/source/components/Header/Header.tsx @@ -6,7 +6,7 @@ import { useSelector } from 'react-redux'; import { INetwork } from '@pollum-io/sysweb3-network'; import { ErrorModal, Icon, Modal, PrimaryButton, SecondaryButton } from '..'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { RootState } from 'state/store'; import { AccountHeader } from '.'; @@ -18,7 +18,7 @@ interface IHeader { } export const Header: React.FC = ({ accountHeader = false }) => { - const { wallet, dapp } = getController(); + const { controllerEmitter } = useController(); const { t } = useTranslation(); const error = useSelector((state: RootState) => state.vault.error); @@ -54,18 +54,26 @@ export const Header: React.FC = ({ accountHeader = false }) => { title: t('header.errorSwitching'), }); - wallet.resolveError(); + controllerEmitter(['wallet', 'resolveError']); } }, [error]); const hanldeDisconnectFromDapp = () => { - dapp.disconnect(host); - wallet.resolveAccountConflict(); + controllerEmitter(['dapp', 'disconnect'], [host]); + controllerEmitter(['wallet', 'resolveAccountConflict']); }; const handleChangeConnectedAccount = () => { - dapp.changeAccount(host, newConnectedAccount.id, connectedAccountType); - wallet.setAccount(newConnectedAccount.id, connectedAccountType); - wallet.resolveAccountConflict(); + controllerEmitter( + ['dapp', 'changeAccount'], + [host, newConnectedAccount.id, connectedAccountType] + ); + + controllerEmitter( + ['wallet', 'setAccount'], + [newConnectedAccount.id, connectedAccountType] + ); + + controllerEmitter(['wallet', 'resolveAccountConflict']); }; return ( @@ -100,7 +108,9 @@ export const Header: React.FC = ({ accountHeader = false }) => { wallet.resolveAccountConflict()} + onClose={() => + controllerEmitter(['wallet', 'resolveAccountConflict']) + } >
{ - const { wallet, dapp, refresh } = getController(); - + const { controllerEmitter } = useController(); + const { t } = useTranslation(); + const { navigate } = useUtils(); const { changingConnectedAccount: { isChangingConnectedAccount }, advancedSettings, } = useSelector((state: RootState) => state.vault); - const { t } = useTranslation(); - const { navigate } = useUtils(); - const [currentTab, setCurrentTab] = useState({ host: '', isConnected: false, @@ -28,7 +26,7 @@ export const GeneralMenu: React.FC = () => { const className = currentTab.isConnected ? 'success' : 'error'; const handleLogout = () => { - wallet.lock(); + controllerEmitter(['wallet', 'lock']); navigate('/'); }; @@ -36,10 +34,18 @@ export const GeneralMenu: React.FC = () => { useEffect(() => { const getTabData = async () => { const url = await getTabUrl(); + + if (!url) return; + const host = getHost(url); - const isConnected = dapp.isConnected(host); - setCurrentTab({ host, isConnected }); + + controllerEmitter(['dapp', 'isConnected'], [host]).then( + (isConnected: boolean) => { + setCurrentTab({ host, isConnected }); + } + ); }; + getTabData(); }, []); @@ -47,9 +53,12 @@ export const GeneralMenu: React.FC = () => { if (!isChangingConnectedAccount) { getTabUrl().then(async (url: string) => { const host = getHost(url); - const isConnected = dapp.isConnected(host); - setCurrentTab({ host, isConnected }); + controllerEmitter(['dapp', 'isConnected'], [host]).then( + (isConnected: boolean) => { + setCurrentTab({ host, isConnected }); + } + ); }); } }, [isChangingConnectedAccount]); @@ -57,7 +66,7 @@ export const GeneralMenu: React.FC = () => { return (
{ {advancedSettings['refresh'] && (
refresh()} + onClick={() => controllerEmitter(['refresh'], [])} className="mx-1.5 hover:text-brand-royalblue text-brand-white cursor-pointer" > diff --git a/source/components/Header/Menus/NetworkMenu.tsx b/source/components/Header/Menus/NetworkMenu.tsx index 383392471..ef009acd0 100644 --- a/source/components/Header/Menus/NetworkMenu.tsx +++ b/source/components/Header/Menus/NetworkMenu.tsx @@ -12,10 +12,8 @@ import btcIcon from 'assets/images/btcIcon.svg'; import ethIcon from 'assets/images/ethIcon.svg'; import { Icon } from 'components/index'; import { useUtils } from 'hooks/index'; -import { - dispatchChangeNetworkBgEvent, - getController, -} from 'scripts/Background'; +import { useController } from 'hooks/useController'; +import { dispatchChangeNetworkBgEvent } from 'scripts/Background'; import { RootState } from 'state/store'; import { NetworkType } from 'utils/types'; @@ -36,7 +34,7 @@ export const NetworkMenu: React.FC = ( props: INetworkComponent ) => { const { setActiveAccountModalIsOpen, setSelectedNetwork } = props; - const { wallet } = getController(); + const { controllerEmitter } = useController(); const { t, i18n } = useTranslation(); const { language } = i18n; const { dapps } = useSelector((state: RootState) => state.dapp); @@ -96,7 +94,8 @@ export const NetworkMenu: React.FC = ( setActiveAccountModalIsOpen(true); return; } - await wallet.setActiveNetwork(network, chain); + await controllerEmitter(['wallet', 'setActiveNetwork'], [network, chain]); + dispatchChangeNetworkBgEvent(network, !!network?.slip44); } catch (networkError) { navigate('/home'); @@ -117,7 +116,7 @@ export const NetworkMenu: React.FC = ( return ( {(menuprops) => ( <> diff --git a/source/components/Header/SetActiveAccountModal.tsx b/source/components/Header/SetActiveAccountModal.tsx index 6b6bd46e3..d51417758 100644 --- a/source/components/Header/SetActiveAccountModal.tsx +++ b/source/components/Header/SetActiveAccountModal.tsx @@ -6,8 +6,8 @@ import { KeyringAccountType } from '@pollum-io/sysweb3-keyring'; import { INetwork } from '@pollum-io/sysweb3-network'; import { Icon, Modal, PrimaryButton, SecondaryButton } from 'components/index'; +import { useController } from 'hooks/useController'; import { useUtils } from 'hooks/useUtils'; -import { getController } from 'scripts/Background'; import { RootState } from 'state/store'; interface ISetActiveAccountModalProps { @@ -17,12 +17,13 @@ interface ISetActiveAccountModalProps { } export const SetActiveAccountModal = (props: ISetActiveAccountModalProps) => { + const { controllerEmitter } = useController(); const { showModal, setIsOpen, selectedNetwork } = props; const { accounts, isBitcoinBased, activeAccount } = useSelector( (state: RootState) => state.vault ); const { t } = useTranslation(); - const { wallet } = getController(); + const { alert } = useUtils(); const [accountId, setAccountId] = useState(activeAccount.id); @@ -41,11 +42,14 @@ export const SetActiveAccountModal = (props: ISetActiveAccountModalProps) => { alert.error(t('header.pleaseSelect')); return; } - wallet.setAccount(accountId, accountType); - await wallet.setActiveNetwork( - selectedNetwork.network, - selectedNetwork.chain + + controllerEmitter(['wallet', 'setAccount'], [accountId, accountType]); + + controllerEmitter( + ['wallet', 'setActiveAccount'], + [selectedNetwork.network, selectedNetwork.chain] ); + setIsOpen(false); }; diff --git a/source/components/Modal/WalletProviderDafault/hooks/useWalletProviderDefault.tsx b/source/components/Modal/WalletProviderDafault/hooks/useWalletProviderDefault.tsx index c110769e6..5e5a24916 100644 --- a/source/components/Modal/WalletProviderDafault/hooks/useWalletProviderDefault.tsx +++ b/source/components/Modal/WalletProviderDafault/hooks/useWalletProviderDefault.tsx @@ -6,17 +6,16 @@ import { getContainerStyleAnimation, getIconStyleAnimation, } from '../utils/getModalStyleAnimation'; +import { useController } from 'hooks/useController'; import { RootState } from 'state/store'; -import { getController } from 'utils/browser'; export const useWalletProviderDefault = () => { + const { controllerEmitter } = useController(); const { hasEthProperty } = useSelector((state: RootState) => state.vault); const [isHovered, setIsHovered] = useState(false); const [isEnabled, setIsEnabled] = useState(hasEthProperty); const [isNotVisible, setIsNotVisible] = useState(!!isEnabled); - const controller = getController(); - const containerStyleAnimation = getContainerStyleAnimation( isHovered, isNotVisible @@ -27,19 +26,28 @@ export const useWalletProviderDefault = () => { const turnPaliAsDefault = () => { setIsEnabled(!isEnabled); if (isEnabled) { - controller.wallet.removeWindowEthProperty(); - controller.wallet.setHasEthProperty(false); - const dapps = Object.values(controller.dapp.getAll()); - // disconnect from all dapps when remove window.ethereum property - if (dapps.length) { - for (const dapp of dapps) { - if (controller.dapp.isConnected(dapp.host)) - controller.dapp.disconnect(dapp.host); + controllerEmitter(['wallet', 'removeWindowEthProperty']); + + controllerEmitter(['wallet', 'setHasEthProperty'], [false]); + + controllerEmitter(['dapp', 'getAll']).then(async (response) => { + const dapps = Object.values(response); + + if (dapps.length) { + for (const dapp of dapps) { + await controllerEmitter(['dapp', 'isConnected'], [dapp.host]).then( + async (isConnected: boolean) => { + if (isConnected) { + await controllerEmitter(['dapp', 'disconnect'], [dapp.host]); + } + } + ); + } } - } + }); } else { - controller.wallet.addWindowEthProperty(); - controller.wallet.setHasEthProperty(true); + controllerEmitter(['wallet', 'addWindowEthProperty']); + controllerEmitter(['wallet', 'setHasEthProperty'], [true]); } }; diff --git a/source/components/TransactionOptions/TransactionOptions.tsx b/source/components/TransactionOptions/TransactionOptions.tsx index 736f3f903..e773ce8a3 100644 --- a/source/components/TransactionOptions/TransactionOptions.tsx +++ b/source/components/TransactionOptions/TransactionOptions.tsx @@ -13,7 +13,6 @@ export const TransactionOptions: React.FC = ({ transaction, alert, chainId, - wallet, setIsOpenModal, setModalData, }) => { @@ -41,7 +40,6 @@ export const TransactionOptions: React.FC = ({ isLegacy: isLegacyTransaction, txHash: transaction.hash, updateType: UpdateTxAction.Cancel, - wallet, }, }); setIsOpenModal(false); @@ -62,7 +60,6 @@ export const TransactionOptions: React.FC = ({ isLegacy: isLegacyTransaction, txHash: transaction.hash, updateType: UpdateTxAction.SpeedUp, - wallet, }, }); setIsOpenModal(false); diff --git a/source/config/consts.js b/source/config/consts.js index 81fc7e072..7a0782f5a 100644 --- a/source/config/consts.js +++ b/source/config/consts.js @@ -89,6 +89,7 @@ const MV3_OPTIONS = { 'clipboardWrite', 'unlimitedStorage', 'offscreen', + 'scripting', ], host_permissions: [ 'http://*/*', diff --git a/source/config/generateManifest.js b/source/config/generateManifest.js index 0e982ae23..9c6039f72 100644 --- a/source/config/generateManifest.js +++ b/source/config/generateManifest.js @@ -2,6 +2,7 @@ const dotenv = require('dotenv'); const fs = require('fs'); +const path = require('path'); const { MV2_OPTIONS, MV3_OPTIONS } = require('./consts.js'); @@ -12,6 +13,26 @@ function generateManifest() { const manifestOptions = process.env.MANIFEST_TYPE === 'MV2' ? MV2_OPTIONS : MV3_OPTIONS; + // dev + if (process.env.NODE_ENV === 'development') { + delete manifestOptions.environment; + if (!manifestOptions.permissions) { + manifestOptions.permissions = []; + } + if (!manifestOptions.permissions.includes('webRequest')) { + manifestOptions.permissions.push('webRequest'); + } + } + + // prod + if (process.env.NODE_ENV === 'production') { + if (manifestOptions.permissions) { + manifestOptions.permissions = manifestOptions.permissions.filter( + (permission) => permission !== 'webRequest' + ); + } + } + const manifestContent = JSON.stringify(manifestOptions, null, 2); fs.writeFileSync('manifest.json', manifestContent); } diff --git a/source/config/manifest.json b/source/config/manifest.json new file mode 100644 index 000000000..087627c4a --- /dev/null +++ b/source/config/manifest.json @@ -0,0 +1,87 @@ +{ + "manifest_version": 3, + "name": "Pali Wallet", + "version": "2.0.18", + "icons": { + "16": "assets/icons/favicon-16.png", + "32": "assets/icons/favicon-32.png", + "48": "assets/icons/favicon-48.png", + "128": "assets/icons/favicon-128.png" + }, + "description": "A Non-Custodial Crypto Wallet", + "short_name": "pali", + "permissions": [ + "storage", + "activeTab", + "clipboardWrite", + "unlimitedStorage", + "offscreen" + ], + "host_permissions": [ + "http://*/*", + "https://*/*", + "*://connect.trezor.io/9/*", + "*://*.eth/", + "http://localhost:8545/" + ], + "content_security_policy": { + "extension_pages": "script-src 'self'; object-src 'self'" + }, + "author": "pollum labs", + "minimum_chrome_version": "49", + "action": { + "default_popup": "app.html", + "default_icon": { + "16": "assets/icons/favicon-16.png", + "32": "assets/icons/favicon-32.png", + "48": "assets/icons/favicon-48.png", + "128": "assets/icons/favicon-128.png" + }, + "default_title": "Pali Wallet" + }, + "background": { + "service_worker": "js/background.bundle.js" + }, + "content_scripts": [ + { + "all_frames": true, + "matches": [ + "file://*/*", + "http://*/*", + "https://*/*" + ], + "run_at": "document_start", + "js": [ + "js/contentScript.bundle.js" + ] + }, + { + "matches": [ + "*://connect.trezor.io/9/popup.html", + "https://localhost:8088/*" + ], + "js": [ + "js/trezorScript.bundle.js" + ] + } + ], + "web_accessible_resources": [ + { + "resources": [ + "js/inpage.bundle.js", + "js/handleWindowProperties.bundle.js", + "js/pali.bundle.js" + ], + "matches": [ + "" + ] + } + ], + "commands": { + "_execute_action": { + "suggested_key": { + "default": "Ctrl+Shift+P" + } + } + } +} \ No newline at end of file diff --git a/source/config/webpack.common.js b/source/config/webpack.common.js new file mode 100644 index 000000000..7ea45b53d --- /dev/null +++ b/source/config/webpack.common.js @@ -0,0 +1,201 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ + +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); +const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); +const { join, resolve } = require('path'); +const TerserPlugin = require('terser-webpack-plugin'); +const { DefinePlugin } = require('webpack'); + +const viewsPath = join(__dirname, '../../views'); +const sourcePath = join(__dirname, '../../source'); +const destPath = join(__dirname, '../../build'); +const targetBrowser = process.env.TARGET_BROWSER || 'chrome'; + +module.exports = { + entry: { + manifest: join(__dirname, '../../manifest.json'), + background: join(sourcePath, 'scripts/Background', 'index.ts'), + inpage: join(sourcePath, 'scripts/ContentScript', 'inject/inpage.ts'), + pali: join(sourcePath, 'scripts/ContentScript', 'inject/pali.ts'), + handleWindowProperties: join( + sourcePath, + 'scripts/ContentScript', + 'inject/handleWindowProperties.ts' + ), + contentScript: join(sourcePath, 'scripts/ContentScript', 'index.ts'), + app: join(sourcePath, 'pages/App', 'index.tsx'), + external: join(sourcePath, 'pages/External', 'index.tsx'), + trezorScript: join( + sourcePath, + 'scripts/ContentScript/trezor', + 'trezor-content-script.ts' + ), + trezorUSB: join( + sourcePath, + 'scripts/ContentScript/trezor', + 'trezor-usb-permissions.ts' + ), + offscreenScript: join( + sourcePath, + 'scripts/ContentScript/offscreen', + 'index.ts' + ), + }, + output: { + path: join(destPath, targetBrowser), + filename: 'js/[name].bundle.js', + clean: true, + }, + resolve: { + extensions: ['.ts', '.tsx', '.js', '.json'], + alias: { + assets: resolve(__dirname, '../../source/assets'), + components: resolve(__dirname, '../../source/components'), + scripts: resolve(__dirname, '../../source/scripts'), + containers: resolve(__dirname, '../../source/containers'), + pages: resolve(__dirname, '../../source/pages'), + routers: resolve(__dirname, '../../source/routers'), + state: resolve(__dirname, '../../source/state'), + constants: resolve(__dirname, '../../source/constants'), + services: resolve(__dirname, '../../source/services'), + hooks: resolve(__dirname, '../../source/hooks'), + utils: resolve(__dirname, '../../source/utils'), + helpers: resolve(__dirname, '../../source/helpers'), + }, + fallback: { + fs: false, + }, + }, + module: { + rules: [ + { + test: /\.(jpg|png|xlsx|xls|csv)$/i, + type: 'asset/resource', + generator: { + filename: '[path][name][ext]', + }, + exclude: /node_modules/, + }, + { + test: /\.(svg)$/i, + type: 'asset/inline', + exclude: /node_modules/, + }, + { + test: /\.(js|ts)x?$/, + loader: 'babel-loader', + exclude: /node_modules/, + }, + { + test: /\.less$/, + use: [ + 'style-loader', + 'css-loader', + { + loader: 'less-loader', + options: { + lessOptions: { + modifyVars: { + 'primary-color': '#1DA57A', + 'link-color': '#1DA57A', + 'border-radius-base': '2rem', + }, + javascriptEnabled: true, + }, + }, + }, + ], + }, + { + test: /\.(ttf)$/, + type: 'asset/resource', + generator: { + filename: '[path][name][ext]', + }, + }, + { + test: /\.(sa|sc|c)ss$/, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + 'postcss-loader', + 'resolve-url-loader', + 'sass-loader', + ], + }, + ], + }, + plugins: [ + new ForkTsCheckerWebpackPlugin(), + new DefinePlugin({ + NODE_ENV: JSON.stringify(process.env.NODE_ENV), + TARGET_BROWSER: JSON.stringify(targetBrowser), + }), + new HtmlWebpackPlugin({ + template: join(viewsPath, 'app.html'), + inject: 'body', + chunks: ['app'], + hash: true, + filename: 'app.html', + }), + new HtmlWebpackPlugin({ + template: join(viewsPath, 'external.html'), + inject: 'body', + chunks: ['external'], + hash: true, + filename: 'external.html', + }), + new HtmlWebpackPlugin({ + template: join(viewsPath, 'trezor-usb-permissions.html'), + filename: 'trezor-usb-permissions.html', + inject: 'body', + chunks: ['trezorUSB'], + }), + new HtmlWebpackPlugin({ + template: join(viewsPath, 'offscreen.html'), + filename: 'offscreen.html', + inject: 'body', + chunks: ['offscreenScript'], + }), + new MiniCssExtractPlugin({ filename: 'css/[name].css' }), + new CopyWebpackPlugin({ + patterns: [{ from: 'source/assets', to: 'assets' }], + }), + new CopyWebpackPlugin({ + patterns: [ + { + from: './manifest.json', + to: join(__dirname, '../../build/chrome'), + force: true, + transform: function (content) { + return Buffer.from( + JSON.stringify({ ...JSON.parse(content.toString()) }) + ); + }, + }, + ], + }), + new NodePolyfillPlugin(), + ], + optimization: { + minimizer: [ + new CssMinimizerPlugin(), + new TerserPlugin({ + parallel: true, + terserOptions: { + compress: { + drop_console: true, + }, + output: { + comments: false, + }, + }, + extractComments: false, + }), + ], + }, +}; diff --git a/source/config/webpack.dev.js b/source/config/webpack.dev.js new file mode 100644 index 000000000..f1379ae4c --- /dev/null +++ b/source/config/webpack.dev.js @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ + +const { merge } = require('webpack-merge'); + +const common = require('./webpack.common'); + +module.exports = merge(common, { + mode: 'development', + devtool: 'inline-source-map', + devServer: { + compress: true, + port: 9090, + hot: true, + watchFiles: ['*'], + }, +}); diff --git a/source/config/webpack.prod.js b/source/config/webpack.prod.js new file mode 100644 index 000000000..27149c16b --- /dev/null +++ b/source/config/webpack.prod.js @@ -0,0 +1,39 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ + +const path = require('path'); +const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); +const { merge } = require('webpack-merge'); +const ZipPlugin = require('zip-webpack-plugin'); + +const common = require('./webpack.common'); + +const version = require('../../package.json').version; + +const targetBrowser = process.env.TARGET_BROWSER || 'chrome'; + +module.exports = merge(common, { + mode: 'production', + devtool: 'source-map', + plugins: [ + new ZipPlugin({ + filename: `pali-wallet-${targetBrowser}-${version}`, + path: path.join(__dirname, '../../build'), + extension: `${ + targetBrowser === 'opera' + ? 'crx' + : targetBrowser === 'firefox' + ? 'xpi' + : 'zip' + }`, + }), + new BundleAnalyzerPlugin({ + analyzerMode: 'static', + openAnalyzer: false, + reportFilename: path.join( + __dirname, + '../../build', + `${targetBrowser}-report.html` + ), + }), + ], +}); diff --git a/source/hooks/useController.ts b/source/hooks/useController.ts new file mode 100644 index 000000000..d8b693dc6 --- /dev/null +++ b/source/hooks/useController.ts @@ -0,0 +1,66 @@ +import { useCallback, useEffect, useMemo, useState } from 'react'; + +import { CustomJsonRpcProvider } from '@pollum-io/sysweb3-keyring'; +import { INetwork } from '@pollum-io/sysweb3-network'; + +import { controllerEmitter } from 'scripts/Background/controllers/controllerEmitter'; +// import { rehydrateStore } from 'state/rehydrate'; +// import store from 'state/store'; + +export function useController() { + const [isUnlocked, setIsUnlocked] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [activeNetwork, setActiveNetwork] = useState(null); + const [web3Provider, setWeb3Provider] = useState({ + serverHasAnError: false, + errorMessage: '', + } as CustomJsonRpcProvider); + + const abortController = new AbortController(); + + const fetchControllerData = useCallback( + async (shouldSetIsLoading = true) => { + if (shouldSetIsLoading) setIsLoading(true); + + const network = (await controllerEmitter([ + 'wallet', + 'getNetwork', + ])) as INetwork; + + const _web3Provider = new CustomJsonRpcProvider( + abortController.signal, + network.url + ); + + const _isUnlocked = await controllerEmitter(['wallet', 'isUnlocked'], []); + + setActiveNetwork(network); + + setWeb3Provider(_web3Provider); + + setIsUnlocked(!!_isUnlocked); + + setIsLoading(false); + }, + [setIsUnlocked, web3Provider, setIsLoading, controllerEmitter] + ); + + useEffect(() => { + fetchControllerData(); + + return () => { + fetchControllerData(); + }; + }, []); + + return useMemo( + () => ({ + isUnlocked, + web3Provider, + activeNetwork, + isLoading, + controllerEmitter, + }), + [isUnlocked, web3Provider, activeNetwork, isLoading, controllerEmitter] + ); +} diff --git a/source/pages/Chain/ChainErrorPage.tsx b/source/pages/Chain/ChainErrorPage.tsx index c9dd496a6..128772510 100644 --- a/source/pages/Chain/ChainErrorPage.tsx +++ b/source/pages/Chain/ChainErrorPage.tsx @@ -8,19 +8,22 @@ import rolluxChainImg from 'assets/images/rolluxChain.png'; import sysChainImg from 'assets/images/sysChain.svg'; import { Button } from 'components/Button'; import { Header } from 'components/Header'; +import { useController } from 'hooks/useController'; import { useUtils } from 'hooks/useUtils'; -import { getController } from 'scripts/Background'; import { RootState } from 'state/store'; export const ChainErrorPage = () => { + const { controllerEmitter } = useController(); + const { navigate } = useUtils(); + const { t } = useTranslation(); const activeNetwork = useSelector( (state: RootState) => state.vault.activeNetwork ); - const { navigate } = useUtils(); - const { wallet } = getController(); - const { t } = useTranslation(); const handleRetryToConnect = async () => { - await wallet.setActiveNetwork(activeNetwork, String(activeNetwork.chainId)); + await controllerEmitter( + ['wallet', 'setActiveNetwork'], + [activeNetwork, String(activeNetwork.chainId)] + ); }; const CurrentChains = () => { @@ -112,7 +115,11 @@ export const ChainErrorPage = () => { type="submit" className="bg-transparent rounded-[100px] w-[10.25rem] h-[40px] text-white text-base font-medium border border-white" onClick={() => { - wallet.setIsPaliNetworkChanging(false); + controllerEmitter( + ['wallet', 'setIsPaliNetworkChanging'], + [false] + ); + navigate('/home'); }} > diff --git a/source/pages/Connections/ChangeAccount.tsx b/source/pages/Connections/ChangeAccount.tsx index 630510f48..4bd740740 100644 --- a/source/pages/Connections/ChangeAccount.tsx +++ b/source/pages/Connections/ChangeAccount.tsx @@ -6,21 +6,27 @@ import { KeyringAccountType } from '@pollum-io/sysweb3-keyring'; import { Layout, SecondaryButton, PrimaryButton, Icon } from 'components/index'; import { useQueryData } from 'hooks/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { RootState } from 'state/store'; import { dispatchBackgroundEvent } from 'utils/browser'; import { ellipsis } from 'utils/index'; export const ChangeAccount = () => { + const { controllerEmitter } = useController(); const { accounts, isBitcoinBased } = useSelector( (state: RootState) => state.vault ); - const { dapp, wallet } = getController(); const { host, eventName } = useQueryData(); const { t } = useTranslation(); - const currentAccountId = dapp.get(host).accountId; - const currentAccountType = dapp.get(host).accountType; + let currentAccountId: number; + let currentAccountType: KeyringAccountType; + + controllerEmitter(['dapp', 'get'], [host]).then((res: any) => { + currentAccountId = res?.accountId; + currentAccountType = res?.accountType; + }); + const [accountId, setAccountId] = useState(currentAccountId); const [accountType, setCurrentAccountType] = useState(currentAccountType); @@ -38,12 +44,24 @@ export const ChangeAccount = () => { return; } //this should be passed to constant instead of being hardcoded - if (eventName === 'requestPermissions') - dapp.requestPermissions(host, accountId, accountType); - else dapp.changeAccount(host, accountId, accountType); - wallet.setAccount(accountId, accountType); + if (eventName === 'requestPermissions') { + controllerEmitter( + ['dapp', 'requestPermissions'], + [host, accountId, accountType] + ); + } else { + controllerEmitter( + ['dapp', 'changeAccount'], + [host, accountId, accountType] + ); + } + + controllerEmitter(['wallet', 'setAccount'], [accountId, accountType]); + const response = { accountId, accountType }; + dispatchBackgroundEvent(`${eventName}.${host}`, response); + window.close(); }; diff --git a/source/pages/Connections/ChangeConnectedAccount.tsx b/source/pages/Connections/ChangeConnectedAccount.tsx index 4e6d2f965..298c1640d 100644 --- a/source/pages/Connections/ChangeConnectedAccount.tsx +++ b/source/pages/Connections/ChangeConnectedAccount.tsx @@ -4,31 +4,40 @@ import { useSelector } from 'react-redux'; import { Layout, SecondaryButton, PrimaryButton } from 'components/index'; import { useQueryData } from 'hooks/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { RootState } from 'state/store'; import { dispatchBackgroundEvent } from 'utils/browser'; import { ellipsis } from 'utils/index'; export const ChangeConnectedAccount = () => { + const { controllerEmitter } = useController(); const { t } = useTranslation(); const activeAccount = useSelector( (state: RootState) => state.vault.activeAccount ); const { accounts } = useSelector((state: RootState) => state.vault); - const { dapp, wallet } = getController(); //TODO: validate this const { host, eventName, connectedAccount, accountType } = useQueryData(); const handleConnectedAccount = () => { - wallet.setAccount(connectedAccount.id, accountType); + controllerEmitter( + ['wallet', 'setAccount'], + [connectedAccount.id, accountType] + ); + dispatchBackgroundEvent(`${eventName}.${host}`, true); + window.close(); }; const handleActiveAccount = () => { - dapp.changeAccount(host, activeAccount.id, activeAccount.type); + controllerEmitter( + ['dapp', 'changeAccount'], + [host, activeAccount.id, activeAccount.type] + ); //this should be passed to constant instead of being hardcoded dispatchBackgroundEvent(`${eventName}.${host}`, false); + window.close(); }; @@ -46,7 +55,7 @@ export const ChangeConnectedAccount = () => { {host}{' '} {t('header.hostIsConnected')} {connectedAccount.label} ( {ellipsis(connectedAccount.address)}). - {t('header.yourAcctiveAccountIs')}{' '} + {t('header.yourAcctiveAccountIs')} {accounts[activeAccount.type][activeAccount.id].label} ( {ellipsis(accounts[activeAccount.type][activeAccount.id].address)}). {t('connections.withWitchAccount')} diff --git a/source/pages/Connections/ConnectWallet.tsx b/source/pages/Connections/ConnectWallet.tsx index 2340b5be8..e413c5cd6 100644 --- a/source/pages/Connections/ConnectWallet.tsx +++ b/source/pages/Connections/ConnectWallet.tsx @@ -1,5 +1,5 @@ import { Dialog } from '@headlessui/react'; -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; @@ -14,13 +14,13 @@ import { } from 'components/index'; import trustedApps from 'constants/trustedApps.json'; import { useQueryData } from 'hooks/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { RootState } from 'state/store'; import { dispatchBackgroundEvent } from 'utils/browser'; import { ellipsis } from 'utils/index'; export const ConnectWallet = () => { - const { dapp, wallet } = getController(); + const { controllerEmitter, isUnlocked } = useController(); const { host, chain, chainId, eventName } = useQueryData(); const { t } = useTranslation(); const accounts = useSelector((state: RootState) => state.vault.accounts); @@ -33,25 +33,27 @@ export const ConnectWallet = () => { (state: RootState) => state.vault.isBitcoinBased ); - const currentAccountId = dapp.get(host)?.accountId; - const currentAccountType = dapp.get(host)?.accountType; + const [currentAccountId, setCurrentAccountId] = useState(); + const [currentAccountType, setCurrentAccountType] = + useState(); const [accountId, setAccountId] = useState(currentAccountId); const [accountType, setAccountType] = useState(currentAccountType); const [confirmUntrusted, setConfirmUntrusted] = useState(false); const date = Date.now(); - const isUnlocked = wallet.isUnlocked(); - const handleConnect = () => { - dapp.connect({ host, chain, chainId, accountId, accountType, date }); - wallet.setAccount(accountId, accountType); - dispatchBackgroundEvent( - `${eventName}.${host}`, - // dapp.getAccount(host).address - activeAccount.address + const handleConnect = useCallback(async () => { + await controllerEmitter( + ['dapp', 'connect'], + [{ host, chain, chainId, accountId, accountType, date }] ); + + await controllerEmitter(['wallet', 'setAccount'], [accountId, accountType]); + + dispatchBackgroundEvent(`${eventName}.${host}`, activeAccount.address); + window.close(); - }; + }, [host, chain, chainId, accountId, accountType, date]); const onConfirm = () => { const isTrusted = trustedApps.includes(host); @@ -60,18 +62,37 @@ export const ConnectWallet = () => { }; useEffect(() => { - if (dapp.isConnected(host) && isUnlocked) { - dapp.connect( - { host, chain, chainId, accountId, accountType, date: 0 }, - true - ); - dispatchBackgroundEvent( - `${eventName}.${host}`, - dapp.getAccount(host).address + (async () => { + const dapp: any = await controllerEmitter(['dapp', 'get'], [host]); + + if (dapp) { + setCurrentAccountId(dapp?.accountId); + setCurrentAccountType(dapp?.accountType); + } + })(); + }, [accounts]); + + useEffect(() => { + if (isUnlocked && accountType) { + controllerEmitter(['dapp', 'isConnected'], [host]).then( + (isConnected: boolean) => { + if (isConnected) { + controllerEmitter( + ['dapp', 'connect'], + [{ host, chain, chainId, accountId, accountType, date }] + ); + + dispatchBackgroundEvent( + `${eventName}.${host}`, + activeAccount.address + ); + + window.close(); + } + } ); - window.close(); } - }, [isUnlocked]); + }, [isUnlocked, accountType]); return ( { const { navigate } = useUtils(); const { t } = useTranslation(); const { state } = useLocation(); + const { controllerEmitter, isUnlocked } = useController(); //* Selectors const { asset: fiatAsset, price: fiatPrice } = useSelector( @@ -51,15 +52,16 @@ export const Home = () => { //* Constants const { url } = activeNetwork; - const controller = getController(); - const { wallet } = controller; - const { isInCooldown }: CustomJsonRpcProvider = - controller.wallet.ethereumTransaction.web3Provider; + let isInCooldown: boolean; - const isUnlocked = - controller.wallet.isUnlocked() && - accounts[activeAccount.type][activeAccount.id].address !== ''; + controllerEmitter( + ['wallet', 'ethereumTransaction', 'web3Provider'], + [], + true + ).then((response: CustomJsonRpcProvider) => { + isInCooldown = response?.isInCooldown || false; + }); const bgColor = isNetworkChanging ? 'bg-bkg-2' : 'bg-bkg-3'; const { syscoin: syscoinBalance, ethereum: ethereumBalance } = diff --git a/source/pages/Home/Panel/components/Assets/EvmList.tsx b/source/pages/Home/Panel/components/Assets/EvmList.tsx index 6e2a4d18b..bd23cecb9 100644 --- a/source/pages/Home/Panel/components/Assets/EvmList.tsx +++ b/source/pages/Home/Panel/components/Assets/EvmList.tsx @@ -1,18 +1,18 @@ import { uniqueId } from 'lodash'; import React, { Fragment, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { HiTrash as DeleteIcon } from 'react-icons/hi'; import { - FiTrash as DeleteIcon, RiEditLine as EditIcon, RiShareForward2Line as DetailsIcon, -} from 'react-icons/all'; +} from 'react-icons/ri'; import { useSelector } from 'react-redux'; import { EvmNftsList } from '../Nfts/EvmNftsList'; import { LoadingComponent } from 'components/Loading'; import { Tooltip } from 'components/Tooltip'; import { useUtils } from 'hooks/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { RootState } from 'state/store'; import { ITokenEthProps } from 'types/tokens'; import { truncate } from 'utils/index'; @@ -25,10 +25,10 @@ interface IDefaultEvmAssets { } const DefaultEvmAssets = ({ searchValue, sortByValue }: IDefaultEvmAssets) => { - const controller = getController(); const { navigate } = useUtils(); - + const { controllerEmitter } = useController(); const { t } = useTranslation(); + const { accounts, activeAccount, @@ -150,11 +150,12 @@ const DefaultEvmAssets = ({ searchValue, sortByValue }: IDefaultEvmAssets) => { className="cursor-pointer hover:text-fields-input-borderfocus" color="text-brand-white" size={16} - onClick={() => - controller.wallet.account.eth.deleteTokenInfo( - token.contractAddress - ) - } + onClick={() => { + controllerEmitter( + ['wallet', 'account', 'eth', 'deleteTokenInfo'], + [token.contractAddress] + ); + }} />
diff --git a/source/pages/Home/Panel/components/Nfts/EvmNftsList.tsx b/source/pages/Home/Panel/components/Nfts/EvmNftsList.tsx index a3c75eeb8..8a0213374 100644 --- a/source/pages/Home/Panel/components/Nfts/EvmNftsList.tsx +++ b/source/pages/Home/Panel/components/Nfts/EvmNftsList.tsx @@ -5,14 +5,14 @@ import { INftsStructure } from '@pollum-io/sysweb3-utils'; import dafaultImage from 'assets/images/pali-blank.png'; import { useUtils } from 'hooks/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { RootState } from 'state/store'; import { nftsVideoFormats } from 'utils/index'; import { getChainImage } from './GetChainImage'; export const EvmNftsList = () => { - const controller = getController(); + const { controllerEmitter } = useController(); const { navigate } = useUtils(); const { accounts, activeAccount, activeNetwork } = useSelector( (state: RootState) => state.vault @@ -26,10 +26,10 @@ export const EvmNftsList = () => { const getUserNfts = async () => { try { - await controller.wallet.fetchAndUpdateNftsState({ - activeAccount, - activeNetwork, - }); + await controllerEmitter( + ['wallet', 'fetchAndUpdateNftsState'], + [activeAccount, activeNetwork] + ); } catch (error) { console.error('Error on get NFTs:', error); } diff --git a/source/pages/Home/Panel/components/Transactions/EVM/EvmDetails.tsx b/source/pages/Home/Panel/components/Transactions/EVM/EvmDetails.tsx index c2ab13bfd..c1021488f 100644 --- a/source/pages/Home/Panel/components/Transactions/EVM/EvmDetails.tsx +++ b/source/pages/Home/Panel/components/Transactions/EVM/EvmDetails.tsx @@ -72,7 +72,9 @@ export const EvmTransactionDetails = ({ hash }: { hash: string }) => { chainId ] as IEvmTransaction[]; - ethereumTransactions?.find((tx: any) => { + ethereumTransactions?.forEach((transaction: any) => { + const tx = { ...transaction }; + tx.value = !!tx.value?.hex ? tx.value?.hex : tx.value; if (tx?.hash !== hash) return null; @@ -106,8 +108,6 @@ export const EvmTransactionDetails = ({ hash }: { hash: string }) => { if (isValid) formattedTransaction.push(formattedValue); } - - return formattedTransaction; }); const formattedTransactionDetails = formattedTransaction diff --git a/source/pages/Home/Panel/components/Transactions/EVM/EvmList.tsx b/source/pages/Home/Panel/components/Transactions/EVM/EvmList.tsx index 06bc0c4ac..a5b4eb6ac 100644 --- a/source/pages/Home/Panel/components/Transactions/EVM/EvmList.tsx +++ b/source/pages/Home/Panel/components/Transactions/EVM/EvmList.tsx @@ -5,9 +5,9 @@ import { useTransactionsListConfig } from '../utils/useTransactionsInfos'; import { ConfirmationModal } from 'components/Modal'; import { StatusModal } from 'components/Modal/StatusModal'; import { TransactionOptions } from 'components/TransactionOptions'; +import { useController } from 'hooks/useController'; import { usePrice } from 'hooks/usePrice'; import { useUtils } from 'hooks/useUtils'; -import { getController } from 'scripts/Background'; import { IEvmTransaction } from 'scripts/Background/controllers/transactions/types'; import { RootState } from 'state/store'; import { ITransactionInfoEvm, modalDataType } from 'types/useTransactionsInfo'; @@ -42,7 +42,7 @@ export const EvmTransactionsList = ({ } = useTransactionsListConfig(userTransactions); const { navigate } = useUtils(); const { getFiatAmount } = usePrice(); - const { wallet } = getController(); + const { controllerEmitter } = useController(); const [modalData, setModalData] = useState(); const [isOpenModal, setIsOpenModal] = useState(false); @@ -64,7 +64,6 @@ export const EvmTransactionsList = ({ handleUpdateTransaction={handleUpdateTransaction} alert={alert} chainId={chainId} - wallet={wallet} transaction={tx as any} setIsOpenModal={setIsOpenModal} setModalData={setModalData} @@ -73,14 +72,7 @@ export const EvmTransactionsList = ({ } return null; }, - [ - handleUpdateTransaction, - alert, - chainId, - wallet, - setIsOpenModal, - setModalData, - ] + [handleUpdateTransaction, alert, chainId, setIsOpenModal, setModalData] ); const EvmTransactionsListComponent = useCallback(({ tx }) => { @@ -170,12 +162,12 @@ export const EvmTransactionsList = ({ return; } if (lastTx?.confirmations === 0) { - wallet.setIsLastTxConfirmed(chainId, false); + controllerEmitter(['wallet', 'setIsLastTxConfirmed'], [chainId, false]); return; } if (lastTx?.confirmations > 0 && !isLastTxConfirmed?.[chainId]) { setShowModal(true); - wallet.setIsLastTxConfirmed(chainId, true); + controllerEmitter(['wallet', 'setIsLastTxConfirmed'], [chainId, true]); } }, [currentAccount]); diff --git a/source/pages/Home/Panel/components/Transactions/List.tsx b/source/pages/Home/Panel/components/Transactions/List.tsx index 540f46f79..674830e56 100644 --- a/source/pages/Home/Panel/components/Transactions/List.tsx +++ b/source/pages/Home/Panel/components/Transactions/List.tsx @@ -8,7 +8,6 @@ import { IconButton } from 'components/IconButton'; import { ConfirmationModal } from 'components/Modal'; import { TransactionOptions } from 'components/TransactionOptions'; import { useUtils } from 'hooks/index'; -import { getController } from 'scripts/Background'; import { RootState } from 'state/store'; import { ellipsis, formatDate, handleUpdateTransaction } from 'utils/index'; @@ -17,7 +16,6 @@ export const TransactionsList = ({ }: { userTransactions: any[]; //todo: adjust type }) => { - const { wallet } = getController(); const { t } = useTranslation(); const { activeNetwork: { chainId }, @@ -127,7 +125,6 @@ export const TransactionsList = ({ handleUpdateTransaction={handleUpdateTransaction} alert={alert} chainId={chainId} - wallet={wallet} transaction={tx} setIsOpenModal={setIsOpenModal} setModalData={setModalData} diff --git a/source/pages/Home/Panel/components/Transactions/UTXO/SyscoinDetails.tsx b/source/pages/Home/Panel/components/Transactions/UTXO/SyscoinDetails.tsx index 76321bd50..27b4427b3 100644 --- a/source/pages/Home/Panel/components/Transactions/UTXO/SyscoinDetails.tsx +++ b/source/pages/Home/Panel/components/Transactions/UTXO/SyscoinDetails.tsx @@ -8,14 +8,14 @@ import { Icon } from 'components/Icon'; import { IconButton } from 'components/IconButton'; import { Tooltip } from 'components/Tooltip'; import { useTransactionsListConfig, useUtils } from 'hooks/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { ISysTransaction } from 'scripts/Background/controllers/transactions/types'; import { RootState } from 'state/store'; import { TransactionsType } from 'state/vault/types'; import { camelCaseToText, ellipsis } from 'utils/index'; export const SyscoinTransactionDetails = ({ hash }: { hash: string }) => { - const controller = getController(); + const { controllerEmitter } = useController(); const { accounts, activeAccount, @@ -51,7 +51,10 @@ export const SyscoinTransactionDetails = ({ hash }: { hash: string }) => { const setTx = async () => setRawTransaction( - await controller.utils.getRawTransaction(activeNetworkUrl, hash) + await controllerEmitter( + ['utils', 'getRawTransaction'], + [activeNetworkUrl, hash] + ) ); useEffect(() => { @@ -84,18 +87,19 @@ export const SyscoinTransactionDetails = ({ hash }: { hash: string }) => { return; } } else { - controller.utils - .getRawTransaction(activeNetworkUrl, item.txid) - .then((response: any) => { - for (const responseVout of response.vout) { - if (responseVout.n === item.vout) { - senders[item.addresses[0]] = { - address: item.addresses[0], - value: item.value, - }; - } + controllerEmitter( + ['utils', 'getRawTransaction'], + [activeNetworkUrl, item.txid] + ).then((response: any) => { + for (const responseVout of response.vout) { + if (responseVout.n === item.vout) { + senders[item.addresses[0]] = { + address: item.addresses[0], + value: item.value, + }; } - }); + } + }); } } } diff --git a/source/pages/Home/Panel/components/Transactions/utils/useTransactionsInfos.tsx b/source/pages/Home/Panel/components/Transactions/utils/useTransactionsInfos.tsx index ab6827b82..328222305 100644 --- a/source/pages/Home/Panel/components/Transactions/utils/useTransactionsInfos.tsx +++ b/source/pages/Home/Panel/components/Transactions/utils/useTransactionsInfos.tsx @@ -189,16 +189,30 @@ export const useTransactionsListConfig = ( return ''; }; - return { - getTxStatus, - getTxStatusIcons, - formatTimeStamp, - formatTimeStampUtxo, - filteredTransactions, - isShowedGroupBar, - txId, - getTxType, - blocktime, - getTokenSymbol, - }; + return useMemo( + () => ({ + getTxStatus, + getTxStatusIcons, + formatTimeStamp, + formatTimeStampUtxo, + filteredTransactions, + isShowedGroupBar, + txId, + getTxType, + blocktime, + getTokenSymbol, + }), + [ + getTxStatus, + getTxStatusIcons, + formatTimeStamp, + formatTimeStampUtxo, + filteredTransactions, + isShowedGroupBar, + txId, + getTxType, + blocktime, + getTokenSymbol, + ] + ); }; diff --git a/source/pages/Import/CreatePass.tsx b/source/pages/Import/CreatePass.tsx index 53e17cae0..9f16574df 100755 --- a/source/pages/Import/CreatePass.tsx +++ b/source/pages/Import/CreatePass.tsx @@ -3,11 +3,10 @@ import { useLocation } from 'react-router-dom'; import { PasswordForm } from 'components/index'; import { useUtils } from 'hooks/index'; -import { getController } from 'scripts/Background'; -// import { getController } from 'utils/browser'; +import { useController } from 'hooks/useController'; export const CreatePasswordImport = () => { - const controller = getController(); + const { controllerEmitter } = useController(); const { state } = useLocation(); const { navigate } = useUtils(); @@ -19,7 +18,7 @@ export const CreatePasswordImport = () => { }); const onSubmit = async ({ password }: { password: string }) => { - await controller.wallet.createWallet(password, phrase); + await controllerEmitter(['wallet', 'createWallet'], [password, phrase]); next(); }; diff --git a/source/pages/Import/ImportPhrase.tsx b/source/pages/Import/ImportPhrase.tsx index 59ec75a65..bf8d78177 100755 --- a/source/pages/Import/ImportPhrase.tsx +++ b/source/pages/Import/ImportPhrase.tsx @@ -5,9 +5,8 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { OnboardingLayout, Button } from 'components/index'; -// import { getController } from 'utils/browser'; import { StatusModal } from 'components/Modal/StatusModal'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { formatSeedPhrase } from 'utils/format'; type SeedValidationType = { @@ -19,7 +18,7 @@ const eyeStyle = 'w-[18px] max-w-none cursor-pointer hover:cursor-pointer z-20'; const ImportPhrase: React.FC = () => { const { TextArea } = Input; - const controller = getController(); + const { controllerEmitter } = useController(); const [form] = useForm(); const navigate = useNavigate(); const { t } = useTranslation(); @@ -42,11 +41,15 @@ const ImportPhrase: React.FC = () => { ); const onSubmit = ({ phrase }: { phrase: string }) => { - if (controller.wallet.isSeedValid(phrase)) { - navigate('/create-password-import', { - state: { phrase, isWalletImported: true }, - }); - } + controllerEmitter(['wallet', 'isSeedValid'], [phrase]).then( + (isSeedValid: boolean) => { + if (isSeedValid) { + navigate('/create-password-import', { + state: { phrase, isWalletImported: true }, + }); + } + } + ); }; const handleKeypress = (event) => { @@ -88,19 +91,27 @@ const ImportPhrase: React.FC = () => { form.setFieldsValue({ phrase: value }); //todo: we should validate the seed phrase with the new fn - setSeedIsValid(controller.wallet.isSeedValid(value) && value); - if (controller.wallet.isSeedValid(value)) { + return controllerEmitter( + ['wallet', 'isSeedValid'], + [value] + ).then((isSeedValid: boolean) => { + if (isSeedValid) { + setSeedIsValid(isSeedValid && value); + + setSeedValidation({ + seedLength: value.seedLength, + seedLengthError: false, + }); + + return Promise.resolve(); + } + setSeedValidation({ seedLength: value.seedLength, - seedLengthError: false, + seedLengthError: value.seedLengthError, }); - return Promise.resolve(); - } - setSeedValidation({ - seedLength: value.seedLength, - seedLengthError: value.seedLengthError, + return Promise.reject(); }); - return Promise.reject(); }, }), ]} diff --git a/source/pages/SeedConfirm/CreatePhrase.tsx b/source/pages/SeedConfirm/CreatePhrase.tsx index 8dced7598..fb22fc8fc 100644 --- a/source/pages/SeedConfirm/CreatePhrase.tsx +++ b/source/pages/SeedConfirm/CreatePhrase.tsx @@ -1,12 +1,13 @@ -import React, { useMemo, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { OnboardingLayout, Button } from 'components/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; export const CreatePhrase = ({ password }: { password: string }) => { - const controller = getController(); + const { controllerEmitter } = useController(); + const [seed, setSeed] = useState(''); const { t } = useTranslation(); const [visible, setVisible] = useState(false); @@ -15,8 +16,12 @@ export const CreatePhrase = ({ password }: { password: string }) => { const navigate = useNavigate(); - //todo: we need to call keyring manager with the new seed phrase function - const seed = useMemo(() => controller.wallet.createNewSeed(), []); + useEffect(() => { + //todo: we need to call keyring manager with the new seed phrase function + controllerEmitter(['wallet', 'createNewSeed']).then((response: string) => { + setSeed(response); + }); + }, []); const handleCopyToClipboard = () => { navigator.clipboard.writeText(seed); diff --git a/source/pages/SeedConfirm/index.tsx b/source/pages/SeedConfirm/index.tsx index 99f98e435..4c5952c8c 100755 --- a/source/pages/SeedConfirm/index.tsx +++ b/source/pages/SeedConfirm/index.tsx @@ -1,14 +1,14 @@ import React, { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; +import { useController } from 'hooks/useController'; import { useUtils } from 'hooks/useUtils'; -import { getController } from 'scripts/Background'; import { ConfirmPhrase } from './ConfirmPhrase'; import { CreatePhrase } from './CreatePhrase'; export const SeedConfirm = () => { - const controller = getController(); + const { controllerEmitter } = useController(); const { navigate } = useUtils(); @@ -20,7 +20,10 @@ export const SeedConfirm = () => { const handleConfirm = async () => { if (passed) { - await controller.wallet.createWallet(password, createdSeed); + await controllerEmitter( + ['wallet', 'createWallet'], + [password, createdSeed] + ); navigate('/home'); } diff --git a/source/pages/Send/Approve.tsx b/source/pages/Send/Approve.tsx index 17e3c428b..955550c93 100644 --- a/source/pages/Send/Approve.tsx +++ b/source/pages/Send/Approve.tsx @@ -1,5 +1,5 @@ import { Form } from 'antd'; -import { ethers } from 'ethers'; +import { BigNumber, ethers } from 'ethers'; import React, { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; @@ -16,8 +16,8 @@ import { IconButton, } from 'components/index'; import { usePrice, useUtils } from 'hooks/index'; +import { useController } from 'hooks/useController'; import { useQueryData } from 'hooks/useQuery'; -import { getController } from 'scripts/Background'; import { RootState } from 'state/store'; import { IApprovedTokenInfos, @@ -35,7 +35,7 @@ import { EditApprovedAllowanceValueModal } from './EditApprovedAllowanceValueMod import { EditPriorityModal } from './EditPriority'; export const ApproveTransactionComponent = () => { - const { wallet } = getController(); + const { controllerEmitter, web3Provider } = useController(); const { t } = useTranslation(); const { getFiatAmount } = usePrice(); @@ -176,7 +176,7 @@ export const ApproveTransactionComponent = () => { ), 9 ), - gasLimit: wallet.ethereumTransaction.toBigNumber( + gasLimit: BigNumber.from( Boolean(customFee.isCustom && customFee.gasLimit > 0) ? customFee.gasLimit : fee.gasLimit @@ -184,10 +184,14 @@ export const ApproveTransactionComponent = () => { }; try { - const response = - await wallet.ethereumTransaction.sendFormattedTransaction(newTxValue); + const response = (await controllerEmitter( + ['wallet', 'ethereumTransaction', 'sendFormattedTransaction'], + [newTxValue] + )) as { hash: string }; + if (activeAccountMeta.type === KeyringAccountType.Trezor) - wallet.sendAndSaveTransaction(response); + controllerEmitter(['wallet', 'sendAndSaveTransaction'], [response]); + setConfirmedDefaultModal(true); setLoading(false); if (isExternal) @@ -248,12 +252,12 @@ export const ApproveTransactionComponent = () => { const abortController = new AbortController(); const getTokenName = async (contractAddress: string) => { - const getProvider = wallet.ethereumTransaction.contentScriptWeb3Provider; + // const getProvider = wallet.ethereumTransaction.contentScriptWeb3Provider; const contractInstance = new ethers.Contract( contractAddress, getErc20Abi(), - getProvider + web3Provider ); const [tokenSymbolByContract, tokenDecimalsByContract] = diff --git a/source/pages/Send/Confirm.tsx b/source/pages/Send/Confirm.tsx index e8b8027b0..33e1aa1d7 100755 --- a/source/pages/Send/Confirm.tsx +++ b/source/pages/Send/Confirm.tsx @@ -1,4 +1,4 @@ -import { ethers } from 'ethers'; +import { BigNumber, ethers } from 'ethers'; import React, { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; @@ -18,7 +18,11 @@ import { } from 'components/index'; import { TxSuccessful } from 'components/Modal/WarningBaseModal'; import { useUtils } from 'hooks/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; +import { + ISysTransaction, + IEvmTransactionResponse, +} from 'scripts/Background/controllers/transactions/types'; import { RootState } from 'state/store'; import { ICustomFeeParams, IFeeState, ITxState } from 'types/transactions'; import { @@ -34,7 +38,7 @@ import { import { EditPriorityModal } from './EditPriority'; export const SendConfirm = () => { - const { wallet, callGetLatestUpdateForAccount } = getController(); + const { controllerEmitter, web3Provider, isLoading } = useController(); const { t } = useTranslation(); const { alert, navigate, useCopyClipboard } = useUtils(); const url = chrome.runtime.getURL('app.html'); @@ -71,8 +75,7 @@ export const SendConfirm = () => { const [confirmedTx, setConfirmedTx] = useState(); const [isEIP1559Compatible, setIsEIP1559Compatible] = useState(); const [copied, copy] = useCopyClipboard(); - const [isReconectModalOpen, setIsReconectModalOpen] = - useState(false); + const [isReconectModalOpen, setIsReconectModalOpen] = useState(false); const basicTxValues = state.tx; @@ -92,16 +95,26 @@ export const SendConfirm = () => { customFee.isCustom && customFee.gasPrice > 0 ) ? customFee.gasPrice * 10 ** 9 // Convert to WEI because injected gasPrices comes in GWEI - : await wallet.ethereumTransaction.getRecommendedGasPrice(); - const gasLimit: any = await wallet.ethereumTransaction.getTxGasLimit( - basicTxValues - ); + : await controllerEmitter([ + 'wallet', + 'ethereumTransaction', + 'getRecommendedGasPrice', + ]).then((gas) => BigNumber.from(gas).toNumber()); + + const gasLimit: any = await controllerEmitter( + ['wallet', 'ethereumTransaction', 'getTxGasLimit'], + [basicTxValues] + ).then((gas) => BigNumber.from(gas).toNumber()); + const initialFee = INITIAL_FEE; - initialFee.gasPrice = Number(correctGasPrice); + + initialFee.gasPrice = correctGasPrice; + setFee({ ...initialFee, gasLimit }); - setGasPrice(Number(correctGasPrice)); - return { gasLimit, gasPrice: Number(correctGasPrice) }; + setGasPrice(correctGasPrice); + + return { gasLimit, gasPrice: correctGasPrice }; }; const handleConfirm = async () => { @@ -122,17 +135,21 @@ export const SendConfirm = () => { case isBitcoinBased === true: try { if (activeAccount.isTrezorWallet) { - await wallet.trezorSigner.init(); - } - if (activeAccount.isLedgerWallet) { - await wallet.ledgerSigner.connectToLedgerDevice(); + await controllerEmitter( + ['wallet', 'trezorSigner', 'init'], + [], + false + ); } - wallet.syscoinTransaction - .sendTransaction( + controllerEmitter( + ['wallet', 'syscoinTransaction', 'sendTransaction'], + [ { ...basicTxValues, fee: 0.00001 }, activeAccount.isTrezorWallet, - activeAccount.isLedgerWallet - ) + activeAccount.isLedgerWallet, + ], + false + ) .then((response) => { setConfirmedTx(response); setConfirmed(true); @@ -140,7 +157,7 @@ export const SendConfirm = () => { //CALL UPDATE TO USER CAN SEE UPDATED BALANCES / TXS AFTER SEND SOME TX setTimeout(() => { - callGetLatestUpdateForAccount(); + controllerEmitter(['callGetLatestUpdateForAccount']); }, 3500); }) .catch((error) => { @@ -200,25 +217,35 @@ export const SendConfirm = () => { if (isEIP1559Compatible === false) { try { - await wallet.ethereumTransaction - .sendFormattedTransaction( + await controllerEmitter( + ['wallet', 'ethereumTransaction', 'sendFormattedTransaction'], + [ { ...restTx, value, gasPrice: ethers.utils.hexlify(gasPrice), - gasLimit: wallet.ethereumTransaction.toBigNumber( + gasLimit: BigNumber.from( validateCustomGasLimit ? customFee.gasLimit : fee.gasLimit ), }, - !isEIP1559Compatible - ) + !isEIP1559Compatible, + ] + ) .then((response) => { if (activeAccountMeta.type === KeyringAccountType.Trezor) - wallet.sendAndSaveTransaction(response); + controllerEmitter( + ['wallet', 'sendAndSaveTransaction'], + [response] + ); + + console.log('response', response); + setConfirmedTx(response); + setConfirmed(true); + setLoading(false); }) .catch((error) => { @@ -267,44 +294,59 @@ export const SendConfirm = () => { } } - wallet.ethereumTransaction - .sendFormattedTransaction({ - ...restTx, - value, - maxPriorityFeePerGas: ethers.utils.parseUnits( - String( - Boolean( - customFee.isCustom && customFee.maxPriorityFeePerGas > 0 - ) - ? customFee.maxPriorityFeePerGas.toFixed(9) - : fee.maxPriorityFeePerGas.toFixed(9) - ), - 9 - ), - maxFeePerGas: ethers.utils.parseUnits( - String( - Boolean(customFee.isCustom && customFee.maxFeePerGas > 0) - ? customFee.maxFeePerGas.toFixed(9) - : fee.maxFeePerGas.toFixed(9) - ), - 9 - ), - gasLimit: wallet.ethereumTransaction.toBigNumber( - validateCustomGasLimit - ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value - : fee.gasLimit - ), - }) + ( + controllerEmitter( + ['wallet', 'ethereumTransaction', 'sendFormattedTransaction'], + [ + { + ...restTx, + value, + maxPriorityFeePerGas: ethers.utils.parseUnits( + String( + Boolean( + customFee.isCustom && + customFee.maxPriorityFeePerGas > 0 + ) + ? customFee.maxPriorityFeePerGas.toFixed(9) + : fee.maxPriorityFeePerGas.toFixed(9) + ), + 9 + ), + maxFeePerGas: ethers.utils.parseUnits( + String( + Boolean( + customFee.isCustom && customFee.maxFeePerGas > 0 + ) + ? customFee.maxFeePerGas.toFixed(9) + : fee.maxFeePerGas.toFixed(9) + ), + 9 + ), + gasLimit: BigNumber.from( + validateCustomGasLimit + ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value + : fee.gasLimit + ), + }, + ] + ) as Promise + ) .then((response) => { if (activeAccountMeta.type === KeyringAccountType.Trezor) - wallet.sendAndSaveTransaction(response); + controllerEmitter( + ['wallet', 'sendAndSaveTransaction'], + [response] + ); + setConfirmedTx(response); + setConfirmed(true); + setLoading(false); //CALL UPDATE TO USER CAN SEE UPDATED BALANCES / TXS AFTER SEND SOME TX setTimeout(() => { - callGetLatestUpdateForAccount(); + controllerEmitter(['callGetLatestUpdateForAccount']); }, 3500); }) .catch((error: any) => { @@ -357,31 +399,47 @@ export const SendConfirm = () => { case false: if (isEIP1559Compatible === false) { try { - wallet.ethereumTransaction - .sendSignedErc20Transaction({ - networkUrl: activeNetwork.url, - receiver: txObjectState.to, - tokenAddress: basicTxValues.token.contractAddress, - tokenAmount: `${basicTxValues.amount}`, - isLegacy: !isEIP1559Compatible, - decimals: basicTxValues?.token?.decimals, - gasPrice: ethers.utils.hexlify(gasPrice), - gasLimit: wallet.ethereumTransaction.toBigNumber( - validateCustomGasLimit - ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value - : fee.gasLimit * 4 - ), - }) + ( + controllerEmitter( + [ + 'wallet', + 'ethereumTransaction', + 'sendSignedErc20Transaction', + ], + [ + { + networkUrl: activeNetwork.url, + receiver: txObjectState.to, + tokenAddress: basicTxValues.token.contractAddress, + tokenAmount: `${basicTxValues.amount}`, + isLegacy: !isEIP1559Compatible, + decimals: basicTxValues?.token?.decimals, + gasPrice: ethers.utils.hexlify(gasPrice), + gasLimit: BigNumber.from( + validateCustomGasLimit + ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value + : fee.gasLimit * 4 + ), + }, + ] + ) as Promise + ) .then(async (response) => { if (activeAccountMeta.type === KeyringAccountType.Trezor) - wallet.sendAndSaveTransaction(response); + controllerEmitter( + ['wallet', 'sendAndSaveTransaction'], + [response] + ); + setConfirmed(true); + setLoading(false); + setConfirmedTx(response); //CALL UPDATE TO USER CAN SEE UPDATED BALANCES / TXS AFTER SEND SOME TX setTimeout(() => { - callGetLatestUpdateForAccount(); + controllerEmitter(['callGetLatestUpdateForAccount']); }, 3500); }) .catch((error) => { @@ -436,51 +494,68 @@ export const SendConfirm = () => { break; } try { - wallet.ethereumTransaction - .sendSignedErc20Transaction({ - networkUrl: activeNetwork.url, - receiver: txObjectState.to, - tokenAddress: basicTxValues.token.contractAddress, - tokenAmount: `${basicTxValues.amount}`, - isLegacy: !isEIP1559Compatible, - decimals: basicTxValues?.token?.decimals, - maxPriorityFeePerGas: ethers.utils.parseUnits( - String( - Boolean( - customFee.isCustom && - customFee.maxPriorityFeePerGas > 0 - ) - ? customFee.maxPriorityFeePerGas.toFixed(9) - : fee.maxPriorityFeePerGas.toFixed(9) - ), - 9 - ), - maxFeePerGas: ethers.utils.parseUnits( - String( - Boolean( - customFee.isCustom && customFee.maxFeePerGas > 0 - ) - ? customFee.maxFeePerGas.toFixed(9) - : fee.maxFeePerGas.toFixed(9) - ), - 9 - ), - gasLimit: wallet.ethereumTransaction.toBigNumber( - validateCustomGasLimit - ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value - : fee.gasLimit * 4 - ), - }) + ( + controllerEmitter( + [ + 'wallet', + 'ethereumTransaction', + 'sendSignedErc20Transaction', + ], + [ + { + networkUrl: activeNetwork.url, + receiver: txObjectState.to, + tokenAddress: basicTxValues.token.contractAddress, + tokenAmount: `${basicTxValues.amount}`, + isLegacy: !isEIP1559Compatible, + decimals: basicTxValues?.token?.decimals, + maxPriorityFeePerGas: ethers.utils.parseUnits( + String( + Boolean( + customFee.isCustom && + customFee.maxPriorityFeePerGas > 0 + ) + ? customFee.maxPriorityFeePerGas.toFixed(9) + : fee.maxPriorityFeePerGas.toFixed(9) + ), + 9 + ), + maxFeePerGas: ethers.utils.parseUnits( + String( + Boolean( + customFee.isCustom && customFee.maxFeePerGas > 0 + ) + ? customFee.maxFeePerGas.toFixed(9) + : fee.maxFeePerGas.toFixed(9) + ), + 9 + ), + gasLimit: BigNumber.from( + validateCustomGasLimit + ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value + : fee.gasLimit * 4 + ), + }, + ] + ) as Promise + ) + .then(async (response) => { if (activeAccountMeta.type === KeyringAccountType.Trezor) - wallet.sendAndSaveTransaction(response); + controllerEmitter( + ['wallet', 'sendAndSaveTransaction'], + [response] + ); + setConfirmed(true); + setLoading(false); + setConfirmedTx(response); //CALL UPDATE TO USER CAN SEE UPDATED BALANCES / TXS AFTER SEND SOME TX setTimeout(() => { - callGetLatestUpdateForAccount(); + controllerEmitter(['callGetLatestUpdateForAccount']); }, 3500); }) .catch((error) => { @@ -535,25 +610,34 @@ export const SendConfirm = () => { case true: const { type } = await getContractType( basicTxValues.token.contractAddress, - wallet.ethereumTransaction.web3Provider + web3Provider ); + switch (type) { case 'ERC-721': try { - wallet.ethereumTransaction - .sendSignedErc721Transaction({ - networkUrl: activeNetwork.url, - receiver: txObjectState.to, - tokenAddress: basicTxValues.token.contractAddress, - tokenId: Number(basicTxValues.amount), // Amount is the same field of TokenID at the SendEth Component, - isLegacy: !isEIP1559Compatible, - gasPrice: ethers.utils.hexlify(gasPrice), - gasLimit: wallet.ethereumTransaction.toBigNumber( - validateCustomGasLimit - ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value - : fee.gasLimit * 4 - ), - }) + controllerEmitter( + [ + 'wallet', + 'ethereumTransaction', + 'sendSignedErc721Transaction', + ], + [ + { + networkUrl: activeNetwork.url, + receiver: txObjectState.to, + tokenAddress: basicTxValues.token.contractAddress, + tokenId: Number(basicTxValues.amount), // Amount is the same field of TokenID at the SendEth Component, + isLegacy: !isEIP1559Compatible, + gasPrice: ethers.utils.hexlify(gasPrice), + gasLimit: BigNumber.from( + validateCustomGasLimit + ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value + : fee.gasLimit * 4 + ), + }, + ] + ) .then(async (response) => { setConfirmed(true); setLoading(false); @@ -561,7 +645,7 @@ export const SendConfirm = () => { //CALL UPDATE TO USER CAN SEE UPDATED BALANCES / TXS AFTER SEND SOME TX setTimeout(() => { - callGetLatestUpdateForAccount(); + controllerEmitter(['callGetLatestUpdateForAccount']); }, 3500); }) .catch((error) => { @@ -616,41 +700,49 @@ export const SendConfirm = () => { } case 'ERC-1155': try { - wallet.ethereumTransaction - .sendSignedErc1155Transaction({ - networkUrl: activeNetwork.url, - receiver: txObjectState.to, - tokenAddress: basicTxValues.token.contractAddress, - tokenId: Number(basicTxValues.amount), // Amount is the same field of TokenID at the SendEth Component, - isLegacy: !isEIP1559Compatible, - maxPriorityFeePerGas: ethers.utils.parseUnits( - String( - Boolean( - customFee.isCustom && - customFee.maxPriorityFeePerGas > 0 - ) - ? customFee.maxPriorityFeePerGas.toFixed(9) - : fee.maxPriorityFeePerGas.toFixed(9) + controllerEmitter( + [ + 'wallet', + 'ethereumTransaction', + 'sendSignedErc1155Transaction', + ], + [ + { + networkUrl: activeNetwork.url, + receiver: txObjectState.to, + tokenAddress: basicTxValues.token.contractAddress, + tokenId: Number(basicTxValues.amount), // Amount is the same field of TokenID at the SendEth Component, + isLegacy: !isEIP1559Compatible, + maxPriorityFeePerGas: ethers.utils.parseUnits( + String( + Boolean( + customFee.isCustom && + customFee.maxPriorityFeePerGas > 0 + ) + ? customFee.maxPriorityFeePerGas.toFixed(9) + : fee.maxPriorityFeePerGas.toFixed(9) + ), + 9 ), - 9 - ), - maxFeePerGas: ethers.utils.parseUnits( - String( - Boolean( - customFee.isCustom && customFee.maxFeePerGas > 0 - ) - ? customFee.maxFeePerGas.toFixed(9) - : fee.maxFeePerGas.toFixed(9) + maxFeePerGas: ethers.utils.parseUnits( + String( + Boolean( + customFee.isCustom && customFee.maxFeePerGas > 0 + ) + ? customFee.maxFeePerGas.toFixed(9) + : fee.maxFeePerGas.toFixed(9) + ), + 9 ), - 9 - ), - gasPrice: ethers.utils.hexlify(gasPrice), - gasLimit: wallet.ethereumTransaction.toBigNumber( - validateCustomGasLimit - ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value - : fee.gasLimit * 4 - ), - }) + gasPrice: ethers.utils.hexlify(gasPrice), + gasLimit: BigNumber.from( + validateCustomGasLimit + ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value + : fee.gasLimit * 4 + ), + }, + ] + ) .then(async (response) => { setConfirmed(true); setLoading(false); @@ -658,7 +750,7 @@ export const SendConfirm = () => { //CALL UPDATE TO USER CAN SEE UPDATED BALANCES / TXS AFTER SEND SOME TX setTimeout(() => { - callGetLatestUpdateForAccount(); + controllerEmitter(['callGetLatestUpdateForAccount']); }, 3500); }) .catch((error) => { @@ -748,14 +840,23 @@ export const SendConfirm = () => { const getFeeRecomendation = async () => { try { - const { maxFeePerGas, maxPriorityFeePerGas } = - await wallet.ethereumTransaction.getFeeDataWithDynamicMaxPriorityFeePerGas(); + const { maxFeePerGas, maxPriorityFeePerGas } = (await controllerEmitter( + [ + 'wallet', + 'ethereumTransaction', + 'getFeeDataWithDynamicMaxPriorityFeePerGas', + ] + )) as any; + const initialFeeDetails = { - maxFeePerGas: Number(maxFeePerGas) / 10 ** 9, + maxFeePerGas: BigNumber.from(maxFeePerGas).toNumber() / 10 ** 9, baseFee: - (Number(maxFeePerGas) - Number(maxPriorityFeePerGas)) / 10 ** 9, - maxPriorityFeePerGas: Number(maxPriorityFeePerGas) / 10 ** 9, - gasLimit: wallet.ethereumTransaction.toBigNumber(0), + (BigNumber.from(maxFeePerGas).toNumber() - + BigNumber.from(maxPriorityFeePerGas).toNumber()) / + 10 ** 9, + maxPriorityFeePerGas: + BigNumber.from(maxPriorityFeePerGas).toNumber() / 10 ** 9, + gasLimit: BigNumber.from(0), }; const formattedTxObject = { @@ -768,9 +869,10 @@ export const SendConfirm = () => { setTxObjectState(formattedTxObject); - const getGasLimit = await wallet.ethereumTransaction.getTxGasLimit( - formattedTxObject - ); + const getGasLimit = await controllerEmitter( + ['wallet', 'ethereumTransaction', 'getTxGasLimit'], + [formattedTxObject] + ).then((gas) => BigNumber.from(gas).toNumber()); const finalFeeDetails = { ...initialFeeDetails, @@ -823,16 +925,18 @@ export const SendConfirm = () => { }, [copied]); useEffect(() => { + if (isLoading) return; const validateEIP1559Compatibility = async () => { const isCompatible = await verifyNetworkEIP1559Compatibility( - wallet.ethereumTransaction.web3Provider, + web3Provider, currentBlock ); + setIsEIP1559Compatible(isCompatible); }; validateEIP1559Compatibility(); - }, []); + }, [isLoading, web3Provider]); return ( @@ -841,8 +945,16 @@ export const SendConfirm = () => { title={t('send.txSuccessfull')} phraseOne={t('send.txSuccessfullMessage')} onClose={() => { - wallet.sendAndSaveTransaction(confirmedTx); - wallet.setIsLastTxConfirmed(activeNetwork.chainId, false); + controllerEmitter( + ['wallet', 'sendAndSaveTransaction'], + [confirmedTx] + ); + + controllerEmitter( + ['wallet', 'setIsLastTxConfirmed'], + [activeNetwork.chainId, false] + ); + navigate('/home'); }} /> diff --git a/source/pages/Send/SendEth.tsx b/source/pages/Send/SendEth.tsx index 4960941d4..e98155146 100644 --- a/source/pages/Send/SendEth.tsx +++ b/source/pages/Send/SendEth.tsx @@ -12,7 +12,7 @@ import { isValidEthereumAddress } from '@pollum-io/sysweb3-utils'; import { Card, Layout, Button } from 'components/index'; import { useUtils } from 'hooks/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { RootState } from 'state/store'; import { IERC1155Collection, ITokenEthProps } from 'types/tokens'; import { getAssetBalance, ellipsis } from 'utils/index'; @@ -23,6 +23,7 @@ export const SendEth = () => { const activeNetwork = useSelector( (state: RootState) => state.vault.activeNetwork ); + const { accounts, activeAccount: activeAccountMeta, @@ -39,8 +40,9 @@ export const SendEth = () => { const [isValidAddress, setIsValidAddress] = useState(null); const [isValidAmount, setIsValidAmount] = useState(null); + const { controllerEmitter } = useController(); + const [form] = Form.useForm(); - const { wallet } = getController(); const isAccountImported = accounts[activeAccountMeta.type][activeAccountMeta.id]?.isImported; @@ -160,7 +162,12 @@ export const SendEth = () => { const getFees = async () => { try { const currentGasPrice = - +(await wallet.ethereumTransaction.getRecommendedGasPrice()) / 10 ** 9; + +(await controllerEmitter([ + 'wallet', + 'ethereumTransaction', + 'getRecommendedGasPrice', + ])) / + 10 ** 9; if (currentBlock) { const currentGasLimit = parseInt(currentBlock.gasLimit.toString()) / 10 ** 9; diff --git a/source/pages/Send/SendNTokenTransaction.tsx b/source/pages/Send/SendNTokenTransaction.tsx index 4f8da6fbf..f12a6450f 100644 --- a/source/pages/Send/SendNTokenTransaction.tsx +++ b/source/pages/Send/SendNTokenTransaction.tsx @@ -1,4 +1,4 @@ -import { ethers } from 'ethers'; +import { BigNumber, ethers } from 'ethers'; import omit from 'lodash/omit'; import React, { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -10,7 +10,7 @@ import { IconButton } from 'components/IconButton'; import { Layout, DefaultModal, Button, Icon } from 'components/index'; import { Tooltip } from 'components/Tooltip'; import { useQueryData, useUtils } from 'hooks/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { RootState } from 'state/store'; import { ICustomFeeParams, IFeeState, ITxState } from 'types/transactions'; import { dispatchBackgroundEvent } from 'utils/browser'; @@ -25,9 +25,11 @@ import { import { EditPriorityModal } from './EditPriority'; export const SendNTokenTransaction = () => { - const { - wallet: { ethereumTransaction, sendAndSaveTransaction }, //TODO: validates this gets doesn't leads into bugs - } = getController(); + // const { + // wallet: { ethereumTransaction, sendAndSaveTransaction }, //TODO: validates this gets doesn't leads into bugs + // } + + const { controllerEmitter, web3Provider } = useController(); const { t } = useTranslation(); const { alert, navigate, useCopyClipboard } = useUtils(); const [copied, copy] = useCopyClipboard(); @@ -119,22 +121,32 @@ export const SendNTokenTransaction = () => { customFee.isCustom && customFee.gasPrice > 0 ) ? customFee.gasPrice * 10 ** 9 // Calculate custom value to send to transaction because it comes without decimals, only 8 -> 10 -> 12 - : await ethereumTransaction.getRecommendedGasPrice(); - - await ethereumTransaction - .sendFormattedTransaction( + : await controllerEmitter([ + 'wallet', + 'ethereumTransaction', + 'getRecommendedGasPrice', + ]); + + await controllerEmitter( + ['wallet', 'ethereumTransaction', 'sendFormattedTransaction'], + [ { ...finalLegacyTx, gasPrice: ethers.utils.hexlify(Number(getLegacyGasFee)), - gasLimit: ethereumTransaction.toBigNumber( + gasLimit: BigNumber.from( validateCustomGasLimit ? customFee.gasLimit : fee.gasLimit ), }, - isLegacyTransaction - ) + isLegacyTransaction, + ] + ) .then((response) => { if (activeAccountMeta.type === KeyringAccountType.Trezor) - sendAndSaveTransaction(response); + controllerEmitter( + ['wallet', 'sendAndSaveTransaction'], + [response] + ); + setConfirmedTx(response); setConfirmed(true); setLoading(false); @@ -177,33 +189,37 @@ export const SendNTokenTransaction = () => { } } else { try { - await ethereumTransaction - .sendFormattedTransaction({ - ...txWithoutType, - maxPriorityFeePerGas: ethers.utils.parseUnits( - String( - Boolean( - customFee.isCustom && customFee.maxPriorityFeePerGas > 0 - ) - ? customFee.maxPriorityFeePerGas.toFixed(9) - : fee.maxPriorityFeePerGas.toFixed(9) + await controllerEmitter( + ['wallet', 'ethereumTransaction', 'sendFormattedTransaction'], + [ + { + ...txWithoutType, + maxPriorityFeePerGas: ethers.utils.parseUnits( + String( + Boolean( + customFee.isCustom && customFee.maxPriorityFeePerGas > 0 + ) + ? customFee.maxPriorityFeePerGas.toFixed(9) + : fee.maxPriorityFeePerGas.toFixed(9) + ), + 9 ), - 9 - ), - maxFeePerGas: ethers.utils.parseUnits( - String( - Boolean(customFee.isCustom && customFee.maxFeePerGas > 0) - ? customFee.maxFeePerGas.toFixed(9) - : fee.maxFeePerGas.toFixed(9) + maxFeePerGas: ethers.utils.parseUnits( + String( + Boolean(customFee.isCustom && customFee.maxFeePerGas > 0) + ? customFee.maxFeePerGas.toFixed(9) + : fee.maxFeePerGas.toFixed(9) + ), + 9 ), - 9 - ), - gasLimit: ethereumTransaction.toBigNumber( - validateCustomGasLimit - ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value - : fee.gasLimit - ), - }) + gasLimit: BigNumber.from( + validateCustomGasLimit + ? customFee.gasLimit * 10 ** 9 // Multiply gasLimit to reach correctly decimal value + : fee.gasLimit + ), + }, + ] + ) .then((response) => { setConfirmedTx(response); setConfirmed(true); @@ -253,7 +269,10 @@ export const SendNTokenTransaction = () => { const abortController = new AbortController(); const getInitialFeeRecomendation = async () => { - const nonce = await ethereumTransaction.getRecommendedNonce(tx.from); + const nonce = await controllerEmitter( + ['wallet', 'ethereumTransaction', 'getRecommendedNonce'], + [tx.from] + ); const baseTx = transactionDataValidation ? { from: tx.from, @@ -273,19 +292,22 @@ export const SendNTokenTransaction = () => { let eip1559GasError = false; let gasLimitError = false; if (tx.gas) { - gasLimitResult = ethereumTransaction.toBigNumber(0); + gasLimitResult = BigNumber.from(0); } else { - const currentBlockRequest = - await ethereumTransaction.contentScriptWeb3Provider.send( - 'eth_getBlockByNumber', - ['latest', false] - ); + const currentBlockRequest = (await controllerEmitter( + [ + 'wallet', + 'ethereumTransaction', + 'contentScriptWeb3Provider', + 'send', + ], + ['eth_getBlockByNumber', ['latest', false]] + )) as any; + const gasLimitFromCurrentBlock = Math.floor( Number(currentBlockRequest.gasLimit) * 0.95 ); //GasLimit from current block with 5% discount, whole limit from block is too much - gasLimitResult = ethereumTransaction.toBigNumber( - gasLimitFromCurrentBlock - ); + gasLimitResult = BigNumber.from(gasLimitFromCurrentBlock); gasLimitError = false; // verify tx data @@ -298,9 +320,14 @@ export const SendNTokenTransaction = () => { delete clonedTx.maxPriorityFeePerGas; delete clonedTx.maxFeePerGas; delete clonedTx.gasPrice; - await ethereumTransaction.contentScriptWeb3Provider.send( - 'eth_call', - [clonedTx, 'latest'] + await controllerEmitter( + [ + 'wallet', + 'ethereumTransaction', + 'contentScriptWeb3Provider', + 'send', + ], + ['eth_call', [clonedTx, 'latest']] ); } } catch (error) { @@ -312,8 +339,9 @@ export const SendNTokenTransaction = () => { try { // if tx data is valid, Pali is able to estimate gas. if (!isInvalidTxData) { - gasLimitResult = await ethereumTransaction.getTxGasLimit( - baseTx as any + gasLimitResult = await controllerEmitter( + ['wallet', 'ethereumTransaction', 'getTxGasLimit'], + [baseTx as any] ); } } catch (error) { @@ -325,11 +353,17 @@ export const SendNTokenTransaction = () => { tx.gasLimit = (tx?.gas && Number(tx?.gas) > Number(gasLimitResult)) || (tx?.gasLimit && Number(tx?.gasLimit) > Number(gasLimitResult)) - ? ethereumTransaction.toBigNumber(tx.gas || tx.gasLimit) + ? BigNumber.from(tx.gas || tx.gasLimit) : gasLimitResult; try { - const { maxFeePerGas, maxPriorityFeePerGas } = - await ethereumTransaction.getFeeDataWithDynamicMaxPriorityFeePerGas(); + const { maxFeePerGas, maxPriorityFeePerGas } = (await controllerEmitter( + [ + 'wallet', + 'ethereumTransaction', + 'getFeeDataWithDynamicMaxPriorityFeePerGas', + ] + )) as any; + const feeRecomendation = { maxFeePerGas: tx?.maxFeePerGas ? Number(tx?.maxFeePerGas) / 10 ** 9 @@ -345,7 +379,13 @@ export const SendNTokenTransaction = () => { gasLimit: tx.gasLimit, gasPrice: tx?.gasPrice ? Number(tx.gasPrice) / 10 ** 9 - : Number(await ethereumTransaction.getRecommendedGasPrice()) / + : Number( + await controllerEmitter([ + 'wallet', + 'ethereumTransaction', + 'getRecommendedGasPrice', + ]) + ) / 10 ** 9, }; @@ -394,7 +434,7 @@ export const SendNTokenTransaction = () => { useEffect(() => { const validateEIP1559Compatibility = async () => { const isCompatible = await verifyNetworkEIP1559Compatibility( - ethereumTransaction.contentScriptWeb3Provider, + web3Provider, currentBlock ); setIsEIP1559Compatible(isCompatible); @@ -409,7 +449,10 @@ export const SendNTokenTransaction = () => { title={t('send.txSuccessfull')} description={t('send.txSuccessfullMessage')} onClose={() => { - sendAndSaveTransaction(confirmedTx); + controllerEmitter( + ['wallet', 'sendAndSaveTransaction'], + [confirmedTx] + ); if (isExternal) window.close(); else navigate('/home'); }} diff --git a/source/pages/Send/SendSys.tsx b/source/pages/Send/SendSys.tsx index 2018df941..ac24d7353 100644 --- a/source/pages/Send/SendSys.tsx +++ b/source/pages/Send/SendSys.tsx @@ -12,7 +12,7 @@ import { isValidSYSAddress } from '@pollum-io/sysweb3-utils'; import { Tooltip, Fee, NeutralButton, Layout, Icon } from 'components/index'; import { usePrice, useUtils } from 'hooks/index'; -import { getController } from 'scripts/Background'; +import { useController } from 'hooks/useController'; import { IPriceState } from 'state/price/types'; import { RootState } from 'state/store'; import { ITokenSysProps } from 'types/tokens'; @@ -26,7 +26,7 @@ import { export const SendSys = () => { const { getFiatAmount } = usePrice(); - const controller = getController(); + const { controllerEmitter } = useController(); const { t } = useTranslation(); const { alert, navigate } = useUtils(); const activeNetwork = useSelector( @@ -47,15 +47,15 @@ export const SendSys = () => { const [form] = Form.useForm(); const handleGetFee = useCallback(async () => { - const getRecommendedFee = - await controller.wallet.syscoinTransaction.getRecommendedFee( - activeNetwork.url - ); + const getRecommendedFee = (await controllerEmitter( + ['wallet', 'syscoinTransaction', 'getRecommendedFee'], + [activeNetwork.url] + )) as number; setRecommendedFee(getRecommendedFee || Number(0.00001)); form.setFieldsValue({ fee: getRecommendedFee || Number(0.00001) }); - }, [controller.wallet.account, form]); + }, [activeAccount, form]); const isAccountImported = accounts[activeAccountMeta.type][activeAccountMeta.id]?.isImported; @@ -120,10 +120,10 @@ export const SendSys = () => { const nextStep = async ({ receiver, amount }: any) => { try { setIsLoading(true); - const transactionFee = - await controller.wallet.syscoinTransaction.getEstimateSysTransactionFee( - { amount, receivingAddress: receiver } - ); + const transactionFee = await controllerEmitter( + ['wallet', 'syscoinTransaction', 'getEstimateSysTransactionFee'], + [{ amount, receivingAddress: receiver }] + ); setIsLoading(false); navigate('/send/confirm', { state: { @@ -300,7 +300,7 @@ export const SendSys = () => { as="div" className="scrollbar-styled absolute z-10 left-0 mt-2 py-3 w-44 h-56 text-brand-white font-poppins bg-brand-blue800 border border-fields-input-border focus:border-fields-input-borderfocus rounded-2xl shadow-2xl overflow-auto origin-top-right" > - +