diff --git a/background/lib/gas.ts b/background/lib/gas.ts index edfc5fc369..f45b38c8f0 100644 --- a/background/lib/gas.ts +++ b/background/lib/gas.ts @@ -8,6 +8,7 @@ import Blocknative, { import { BlockPrices, EVMNetwork } from "../networks" import { ARBITRUM_ONE, + BINANCE_SMART_CHAIN, EIP_1559_COMPLIANT_CHAIN_IDS, ETHEREUM, POLYGON, @@ -112,6 +113,39 @@ const getArbitrumPrices = async ( } } +const getLegacyGasPrices = async ( + network: EVMNetwork, + gasPrice: bigint, + blockNumber: number +): Promise => { + return { + network, + blockNumber, + baseFeePerGas: gasPrice, + estimatedPrices: [ + { + confidence: 99, + maxPriorityFeePerGas: 0n, // doesn't exist + maxFeePerGas: 0n, // doesn't exist + price: gasPrice, + }, + { + confidence: 95, + maxPriorityFeePerGas: 0n, + maxFeePerGas: 0n, + price: gasPrice, + }, + { + confidence: 70, + maxPriorityFeePerGas: 0n, + maxFeePerGas: 0n, + price: gasPrice, + }, + ], + dataSource: "local", + } +} + export default async function getBlockPrices( network: EVMNetwork, provider: Provider @@ -156,6 +190,20 @@ export default async function getBlockPrices( return getArbitrumPrices(baseFeePerGas ?? 0n, currentBlock.number) } + if (network.chainID === BINANCE_SMART_CHAIN.chainID) { + try { + const gasPrice = (await provider.getGasPrice()).toBigInt() + + return await getLegacyGasPrices( + BINANCE_SMART_CHAIN, + gasPrice, + currentBlock.number + ) + } catch (err) { + logger.error("Error getting gas price from BlockNative", err) + } + } + // otherwise, we're going it alone! if (feeData.gasPrice === null) { diff --git a/background/lib/nfts.ts b/background/lib/nfts.ts index b11e61399e..287c430623 100644 --- a/background/lib/nfts.ts +++ b/background/lib/nfts.ts @@ -6,6 +6,7 @@ import { AVALANCHE, ARBITRUM_ONE, NETWORK_BY_CHAIN_ID, + BINANCE_SMART_CHAIN, } from "../constants" import { getNFTs as alchemyGetNFTs, AlchemyNFTItem } from "./alchemy" import { getNFTs as simpleHashGetNFTs, SimpleHashNFTModel } from "./simple-hash" @@ -35,6 +36,7 @@ const CHAIN_ID_TO_NFT_METADATA_PROVIDER: { [OPTIMISM.chainID]: ["simplehash"], [ARBITRUM_ONE.chainID]: ["simplehash"], [AVALANCHE.chainID]: ["simplehash"], + [BINANCE_SMART_CHAIN.chainID]: ["simplehash"], } function isGalxeAchievement(url: string | null | undefined) { @@ -69,6 +71,7 @@ const SIMPLE_HASH_CHAIN_TO_ID = { optimism: 10, polygon: 137, arbitrum: 42161, + bsc: 56, } function simpleHashNFTModelToNFT(original: SimpleHashNFTModel): NFT { diff --git a/background/lib/simple-hash.ts b/background/lib/simple-hash.ts index 5da37fc7bb..ff033def56 100644 --- a/background/lib/simple-hash.ts +++ b/background/lib/simple-hash.ts @@ -7,7 +7,7 @@ export type SimpleHashNFTModel = { description?: string token_id: string contract_address: string - chain: "polygon" | "arbitrum" | "optimism" | "ethereum" + chain: "polygon" | "arbitrum" | "optimism" | "ethereum" | "bsc" external_url?: string audio_url: string | null image_url: string | null @@ -24,6 +24,7 @@ const CHAIN_ID_TO_NAME = { 137: "polygon", 42161: "arbitrum", 43114: "avalanche", + 56: "bsc", } /** diff --git a/background/services/name/resolvers/ens.ts b/background/services/name/resolvers/ens.ts index 8823f1ee01..d54a570f44 100644 --- a/background/services/name/resolvers/ens.ts +++ b/background/services/name/resolvers/ens.ts @@ -3,6 +3,7 @@ import { AddressOnNetwork, NameOnNetwork } from "../../../accounts" import { ARBITRUM_ONE, AVALANCHE, + BINANCE_SMART_CHAIN, ETHEREUM, GOERLI, OPTIMISM, @@ -18,6 +19,7 @@ const ENS_SUPPORTED_NETWORKS = [ ARBITRUM_ONE, GOERLI, AVALANCHE, + BINANCE_SMART_CHAIN, ] export default function ensResolverFor( diff --git a/ui/_locales/en/messages.json b/ui/_locales/en/messages.json index 20234fb0b5..5f39aa607d 100644 --- a/ui/_locales/en/messages.json +++ b/ui/_locales/en/messages.json @@ -212,6 +212,10 @@ "explainerThree": "Only in rare cases will the actual fee you pay change by more than 10% from the estimate.", "learnMore": "Learn More" }, + "bsc": { + "title": "Coming soon!", + "description": "Changing network fees for BNB Chain is not currently available." + }, "miner": "Miner:", "maxBase": "Max Base:", "speeds": { diff --git a/ui/components/NetworkFees/FeeSettingsText.tsx b/ui/components/NetworkFees/FeeSettingsText.tsx index 72ccbcef97..4880071c05 100644 --- a/ui/components/NetworkFees/FeeSettingsText.tsx +++ b/ui/components/NetworkFees/FeeSettingsText.tsx @@ -17,6 +17,7 @@ import { import { selectCurrentNetwork } from "@tallyho/tally-background/redux-slices/selectors" import { ARBITRUM_ONE, + BINANCE_SMART_CHAIN, OPTIMISM, ROOTSTOCK, } from "@tallyho/tally-background/constants" @@ -102,6 +103,11 @@ const estimateGweiAmount = (options: { desiredDecimals = 2 } + if (network.chainID === BINANCE_SMART_CHAIN.chainID) { + estimatedSpendPerGas = networkSettings.values.gasPrice ?? 0n + desiredDecimals = 2 + } + const estimatedSpendPerGasInGwei = weiToGwei(estimatedSpendPerGas ?? 0n) const decimalLength = heuristicDesiredDecimalsForUnitPrice( desiredDecimals, diff --git a/ui/components/NetworkFees/NetworkSettingsChooser.tsx b/ui/components/NetworkFees/NetworkSettingsChooser.tsx index 963cb65ea3..f4aa6ec680 100644 --- a/ui/components/NetworkFees/NetworkSettingsChooser.tsx +++ b/ui/components/NetworkFees/NetworkSettingsChooser.tsx @@ -11,6 +11,7 @@ import { } from "@tallyho/tally-background/redux-slices/selectors/transactionConstructionSelectors" import { ARBITRUM_ONE, + BINANCE_SMART_CHAIN, OPTIMISM, ROOTSTOCK, } from "@tallyho/tally-background/constants" @@ -19,6 +20,7 @@ import NetworkSettingsSelect from "./NetworkSettingsSelect" import NetworkSettingsOptimism from "./NetworkSettingsSelectOptimism" import NetworkSettingsRSK from "./NetworkSettingsSelectRSK" import NetworkSettingsSelectArbitrum from "./NetworkSettingsSelectArbitrum" +import NetworkSettingsSelectBNBChain from "./NetworkSettingsSelectBNBChain" interface NetworkSettingsChooserProps { estimatedFeesPerGas: EstimatedFeesPerGas | undefined @@ -59,6 +61,9 @@ export default function NetworkSettingsChooser({ /> ) } + if (transactionDetails.network.name === BINANCE_SMART_CHAIN.name) { + return + } return ( + {t("title")} + {t("description")} + + + ) +} diff --git a/ui/components/SignTransaction/SignTransactionDetailPanel.tsx b/ui/components/SignTransaction/SignTransactionDetailPanel.tsx index 5bf39ad5fb..fd89c844fa 100644 --- a/ui/components/SignTransaction/SignTransactionDetailPanel.tsx +++ b/ui/components/SignTransaction/SignTransactionDetailPanel.tsx @@ -11,7 +11,10 @@ import type { EnrichedLegacyTransactionRequest, } from "@tallyho/tally-background/services/enrichment" import { useTranslation } from "react-i18next" -import { EIP_1559_COMPLIANT_CHAIN_IDS } from "@tallyho/tally-background/constants" +import { + BINANCE_SMART_CHAIN, + EIP_1559_COMPLIANT_CHAIN_IDS, +} from "@tallyho/tally-background/constants" import { useBackgroundDispatch, useBackgroundSelector } from "../../hooks" import FeeSettingsButton from "../NetworkFees/FeeSettingsButton" import NetworkSettingsChooser from "../NetworkFees/NetworkSettingsChooser" @@ -86,18 +89,24 @@ export default function SignTransactionDetailPanel({ setNetworkSettingsModalOpen(false) } + const getHightForSlideUpMenu = () => { + return `${ + transactionDetails.network.name === BINANCE_SMART_CHAIN.name + ? 150 + : 3 * 56 + + 320 + + (hasInsufficientFundsWarning ? 15 : 0) + + (isEIP1559Compliant ? 0 : 40) + }px` + } + return (
setNetworkSettingsModalOpen(false)} - customSize={`${ - 3 * 56 + - 320 + - (hasInsufficientFundsWarning ? 15 : 0) + - (isEIP1559Compliant ? 0 : 40) - }px`} + customSize={getHightForSlideUpMenu()} > void) => { @@ -79,7 +79,7 @@ const findAndReplaceJoeMetamaskOption = (addedNode: Node): void => { ) { // eslint-disable-next-line no-restricted-syntax for (const btn of addedNode.getElementsByTagName("button")) { - if (btn.innerText === META_MASK) { + if (btn.innerText === METAMASK) { maybeButton = btn } } @@ -104,7 +104,7 @@ function findAndReplaceGMXMetamaskOption(addedNode: Node): void { } if ( - addedNode.textContent?.includes(META_MASK) && + addedNode.textContent?.includes(METAMASK) && (addedNode as Element).classList.contains("Connect-wallet-modal") ) { const connectionOptions = (addedNode as Element)?.children?.[1] @@ -117,7 +117,7 @@ function findAndReplaceGMXMetamaskOption(addedNode: Node): void { // eslint-disable-next-line no-restricted-syntax for (const option of connectionOptions) { if (option.classList.contains("MetaMask-btn")) { - option.innerHTML = option.innerHTML.replaceAll(META_MASK, TALLY_NAME) + option.innerHTML = option.innerHTML.replaceAll(METAMASK, TALLY_NAME) // Replace metamask icon with Tally icon option.innerHTML = option.innerHTML.replace( /\ssrc="(.+)"\s/, @@ -182,7 +182,7 @@ function findAndReplaceTofuNftMetamaskOption(addedNode: Node): void { return } - if (addedNode.textContent?.includes(META_MASK)) { + if (addedNode.textContent?.includes(METAMASK)) { const metaMaskContainer = (addedNode as HTMLElement)?.children?.[0] ?.children?.[0]?.children?.[0]?.children?.[0]?.children?.[1] ?.children?.[0]?.children?.[0] as HTMLElement @@ -193,11 +193,11 @@ function findAndReplaceTofuNftMetamaskOption(addedNode: Node): void { const textNode = metaMaskContainer.children[1] - if (!textNode || textNode.textContent !== META_MASK) { + if (!textNode || textNode.textContent !== METAMASK) { return } - textNode.innerHTML = textNode.innerHTML.replace(META_MASK, TALLY_NAME) + textNode.innerHTML = textNode.innerHTML.replace(METAMASK, TALLY_NAME) metaMaskContainer.removeChild(metaMaskContainer.children[0]) const tallyIcon = document.createElement("img") @@ -306,7 +306,7 @@ function findAndReplaceCelerMetamaskOption(addedNode: Node): void { } if (addedNode instanceof HTMLElement) { - if (addedNode.innerText?.includes(META_MASK)) { + if (addedNode.innerText?.includes(METAMASK)) { const modal = addedNode.querySelector(".ant-spin-container") if (modal instanceof HTMLElement) { @@ -315,7 +315,7 @@ function findAndReplaceCelerMetamaskOption(addedNode: Node): void { const textContainer = element.children?.[0]?.children?.[1].children?.[0] - if (textContainer.innerHTML === META_MASK) { + if (textContainer.innerHTML === METAMASK) { const img = element.querySelector("img") if (textContainer && img) { @@ -353,8 +353,8 @@ function findAndReplaceMultchainMetamaskAndInjectedOption( switch (true) { case addedNode.innerText?.includes(INJECTED): return INJECTED - case addedNode.innerText?.includes(META_MASK): - return META_MASK + case addedNode.innerText?.includes(METAMASK): + return METAMASK default: return "" } @@ -384,7 +384,7 @@ function findAndReplaceVenusMetamaskOption(addedNode: Node): void { if ( addedNode instanceof HTMLElement && - addedNode.innerText?.includes(META_MASK) + addedNode.innerText?.includes(METAMASK) ) { /* Replace MetaMak option from account view */ if (addedNode.innerText?.includes("Log out")) { @@ -406,7 +406,7 @@ function findAndReplaceVenusMetamaskOption(addedNode: Node): void { } else { // eslint-disable-next-line no-restricted-syntax for (const btn of addedNode.getElementsByTagName("button")) { - if (btn.innerText === META_MASK) { + if (btn.innerText === METAMASK) { const textContainer = btn.children?.[1] const img = btn.children?.[0] @@ -429,11 +429,11 @@ function findAndReplaceAlpacaFinanceMetamaskOption(addedNode: Node): void { if ( addedNode instanceof HTMLElement && - addedNode.innerText?.includes(META_MASK) + addedNode.innerText?.includes(METAMASK) ) { // eslint-disable-next-line no-restricted-syntax for (const btn of addedNode.getElementsByTagName("button")) { - if (btn.innerText === META_MASK) { + if (btn.innerText === METAMASK) { const textNode = btn.children?.[0]?.children?.[0].children?.[0] const img = btn.querySelector("img")