feat(wallet): implement bridge to aurora button (#14452)

* feat: implement bridge to aurora button

* feedback: Add rel="noopener noreferrer" to link

* feedback: separate opening and closing modal functions

* chore: move openRainbowAppClick up and add openRainbowAppClick to deps list
This commit is contained in:
William Muli
2022-08-04 22:25:51 +03:00
committed by GitHub
parent 8f198b7f97
commit e559c3081f
11 changed files with 276 additions and 9 deletions
@@ -680,7 +680,14 @@ constexpr webui::LocalizedString kLocalizedStrings[] = {
{"braveWalletHelpCenter", IDS_BRAVE_WALLET_HELP_CENTER},
{"braveWalletHelpCenterText", IDS_BRAVE_WALLET_HELP_CENTER_TEXT},
{"braveWalletBuyTapBuyNotSupportedMessage",
IDS_BRAVE_WALLET_BUY_TAB_BUY_NOT_SUPPORTED_MESSAGE}};
IDS_BRAVE_WALLET_BUY_TAB_BUY_NOT_SUPPORTED_MESSAGE},
{"braveWalletAuroraModalTitle", IDS_BRAVE_WALLET_AURORA_MODAL_TITLE},
{"braveWalletAuroraModalDescription",
IDS_BRAVE_WALLET_AURORA_MODAL_DESCRIPTION},
{"braveWalletAuroraModalLearnMore",
IDS_BRAVE_WALLET_AURORA_MODAL_LEARN_MORE},
{"braveWalletAuroraModalOPenButtonText",
IDS_BRAVE_WALLET_AURORA_MODAL_OPEN_BUTTON_TEXT}};
// 0x swap constants
constexpr char kRopstenSwapBaseAPIURL[] = "https://ropsten.api.0x.org/";
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,83 @@
// Copyright (c) 2022 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
import styled from 'styled-components'
import { WalletButton } from '../../../shared/style'
import GlobeConnectIcon from '../../../../assets/svg-icons/globe-connect-icon.svg'
export const modalWidth = '442px'
export const StyledWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
margin: 32px 78px 39px;
`
export const Title = styled.h4`
font-family: 'Poppins';
font-style: normal;
font-weight: 600;
font-size: 18px;
line-height: 26px;
color: ${p => p.theme.color.text02};
text-align: center;
margin: 0 0 7px;
align-self: center;
`
export const Description = styled.p`
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: ${p => p.theme.color.text02};
margin: 0;
`
export const LearnMoreLink = styled.a`
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: ${p => p.theme.color.interactive05};
text-decoration: none;
display: block;
padding-bottom: 56px;
`
export const OpenRainbowAppButton = styled(WalletButton)`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 8px 14px;
height: 40px;
cursor: pointer;
outline: none;
border-radius: 40px;
background-color: ${(p) => p.theme.palette.blurple500};
border: none;
align-self: center;
`
export const ButtonText = styled.span`
font-family: 'Poppins';
font-style: normal;
font-weight: 600;
font-size: 14px;
line-height: 21px;
color: ${(p) => p.theme.palette.white};
`
export const GlobeIcon = styled.div`
width: 24px;
height: 24px;
background: url(${GlobeConnectIcon});
margin-right: 8px;
`
@@ -0,0 +1,54 @@
// Copyright (c) 2022 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
import * as React from 'react'
import { getLocale } from '../../../../../common/locale'
import { PopupModal } from '../..'
import {
ButtonText,
Description,
GlobeIcon,
LearnMoreLink,
modalWidth,
OpenRainbowAppButton,
StyledWrapper,
Title
} from './bridge-to-aurora-modal-styles'
interface Props {
onClose: () => void
onOpenRainbowAppClick: () => void
}
const learnMoreLink = 'https://ethereum.org/en/bridges/#bridge-risk'
export const BridgeToAuroraModal = ({ onClose, onOpenRainbowAppClick }: Props) => {
return (
<PopupModal
title=''
onClose={onClose}
width={modalWidth}
>
<StyledWrapper>
<Title>{getLocale('braveWalletAuroraModalTitle')}</Title>
<Description>{getLocale('braveWalletAuroraModalDescription')}</Description>
<LearnMoreLink
rel='noopener noreferrer'
target='_blank'
href={learnMoreLink}
>
{getLocale('braveWalletAuroraModalLearnMore')}
</LearnMoreLink>
<OpenRainbowAppButton
onClick={onOpenRainbowAppClick}
>
<GlobeIcon />
<ButtonText>{getLocale('braveWalletAuroraModalOPenButtonText')}</ButtonText>
</OpenRainbowAppButton>
</StyledWrapper>
</PopupModal>
)
}
@@ -13,12 +13,13 @@ export interface Props {
children?: React.ReactNode
onClose: () => void
title: string
width?: string
}
const ESC_KEY = 'Escape'
const PopupModal = (props: Props) => {
const { title, onClose, children } = props
const { title, width, onClose, children } = props
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === ESC_KEY) {
@@ -36,7 +37,7 @@ const PopupModal = (props: Props) => {
return (
<StyledWrapper>
<Modal>
<Modal width={width}>
<Header>
<Title>{title}</Title>
<CloseButton onClick={onClose} />
@@ -17,13 +17,13 @@ export const StyledWrapper = styled.div`
backdrop-filter: blur(16px);
`
export const Modal = styled.div`
export const Modal = styled.div<{ width?: string }>`
display: flex;
flex-direction: column;
align-items: center;
justify-conent: center;
min-width: 580px;
max-width: 580px;
min-width: ${p => p.width ? p.width : '580px'};
max-width: ${p => p.width ? p.width : '580px'};
background-color: ${(p) => p.theme.color.background02};
border-radius: 8px;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.25);
@@ -29,6 +29,7 @@ import {
UpdateLoadingMessage, UpdateNFtMetadataMessage,
UpdateSelectedAssetMessage, UpdateTokenNetworkMessage
} from '../../../../nft/nft-ui-messages'
import { auroraSupportedContractAddresses } from '../../../../utils/asset-utils'
// actions
import { WalletPageActions } from '../../../../page/actions'
@@ -42,6 +43,7 @@ import { BackButton } from '../../../shared'
import withPlaceholderIcon from '../../../shared/create-placeholder-icon'
import { ChartControlBar, LineChart } from '../../'
import AccountsAndTransactionsList from './components/accounts-and-transctions-list'
import { BridgeToAuroraModal } from '../../popup-modals/bridge-to-aurora-modal/bridge-to-aurora-modal'
// Hooks
import { useBalance, usePricing, useTransactionParser } from '../../../../common/hooks'
@@ -53,7 +55,7 @@ import {
AssetIcon,
AssetNameText,
AssetRow,
BalanceRow,
BalanceRow, BridgeToAuroraButton,
DetailText,
InfoColumn,
NetworkDescription,
@@ -69,8 +71,13 @@ import {
import { Skeleton } from '../../../shared/loading-skeleton/styles'
const AssetIconWithPlaceholder = withPlaceholderIcon(AssetIcon, { size: 'big', marginLeft: 0, marginRight: 12 })
const rainbowbridgeLink = 'https://rainbowbridge.app'
const bridgeToAuroraWarningShownKey = 'bridgeToAuroraWarningShown'
export const PortfolioAsset = () => {
// state
const [showBridgeToAuroraModal, setShowBridgeToAuroraModal] = React.useState<boolean>(false)
const [bridgeToAuroraWarningShown, setBridgeToAuroraWarningShown] = React.useState<boolean>()
// routing
const history = useHistory()
const { id: assetId, tokenId } = useParams<{ id?: string, tokenId?: string }>()
@@ -174,6 +181,13 @@ export const PortfolioAsset = () => {
: userVisibleTokensInfo.find((token) => token.symbol.toLowerCase() === assetId?.toLowerCase())
}, [assetId, userVisibleTokensInfo, selectedTimeline, tokenId])
const isSelectedAssetBridgeSupported = React.useMemo(() => {
if (!selectedAssetFromParams) return false
return auroraSupportedContractAddresses
.includes(selectedAssetFromParams.contractAddress.toLowerCase()) ||
selectedAssetFromParams.symbol.toUpperCase() === 'ETH'
}, [selectedAssetFromParams])
// This will scrape all of the user's accounts and combine the fiat value for every asset
const fullPortfolioFiatBalance = React.useMemo(() => {
const visibleAssetOptions = userAssetList
@@ -295,6 +309,28 @@ export const PortfolioAsset = () => {
const onNftDetailsLoad = React.useCallback(() => setNftIframeLoaded(true), [])
const onOpenRainbowAppClick = React.useCallback(() => {
chrome.tabs.create({ url: rainbowbridgeLink }, () => {
if (chrome.runtime.lastError) {
console.error('tabs.create failed: ' + chrome.runtime.lastError.message)
}
})
setShowBridgeToAuroraModal(false)
}, [])
const onBridgeToAuroraButton = React.useCallback(() => {
if (bridgeToAuroraWarningShown) {
onOpenRainbowAppClick()
} else {
localStorage.setItem(bridgeToAuroraWarningShownKey, 'true')
setShowBridgeToAuroraModal(true)
}
}, [bridgeToAuroraWarningShown, onOpenRainbowAppClick])
const onCloseAuroraModal = React.useCallback(() => {
setShowBridgeToAuroraModal(false)
}, [])
// effects
React.useEffect(() => {
setfilteredAssetList(userAssetList)
@@ -348,6 +384,10 @@ export const PortfolioAsset = () => {
}
}, [nftIframeLoaded, nftDetailsRef, selectedAsset, nftMetadata, networkList])
React.useEffect(() => {
setBridgeToAuroraWarningShown(localStorage.getItem(bridgeToAuroraWarningShownKey) === 'true')
})
// token list needs to load before we can find an asset to select from the url params
if (userVisibleTokensInfo.length === 0) {
return <Skeleton />
@@ -426,6 +466,21 @@ export const PortfolioAsset = () => {
/>
}
{!isNftAsset && isSelectedAssetBridgeSupported &&
<BridgeToAuroraButton
onClick={onBridgeToAuroraButton}
>
Bridge to Aurora
</BridgeToAuroraButton>
}
{showBridgeToAuroraModal &&
<BridgeToAuroraModal
onClose={onCloseAuroraModal}
onOpenRainbowAppClick={onOpenRainbowAppClick}
/>
}
<NftDetails
onLoad={onNftDetailsLoad}
visible={selectedAsset?.isErc721}
@@ -251,7 +251,29 @@ export const FilterTokenRow = styled.div`
export const NftDetails = styled.iframe<{ visible?: boolean }>`
width: 100%;
min-height: ${p => p.visible ? '490px' : 'hidden'};
min-height: ${p => p.visible ? '490px' : '0px'};
height: ${p => p.visible ? 'auto' : '0px'};
border: none;
visibility: ${p => p.visible ? 'visible' : 'hidden'};
`
export const BridgeToAuroraButton = styled(WalletButton)`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 8px 14px;
height: 40px;
cursor: pointer;
outline: none;
border-radius: 40px;
font-family: 'Poppins';
font-style: normal;
font-weight: 600;
font-size: 14px;
line-height: 21px;
background-color: ${(p) => p.theme.palette.blurple500};
color: ${(p) => p.theme.palette.white};
border: none;
margin-bottom: 32px;
`
+8 -1
View File
@@ -581,5 +581,12 @@ provideStrings({
// Help Center
braveWalletHelpCenter: 'Help Center',
braveWalletHelpCenterText: 'Need help? See'
braveWalletHelpCenterText: 'Need help? See',
// Bridge to Aurora
braveWalletAuroraModalTitle: 'Open rainbowbridge.app',
braveWalletAuroraModalDescription: 'Bridging assets across networks allows you to use your crypto on other networks and other DApp' +
' ecosystems. Bridging assets to other networks has some risks',
braveWalletAuroraModalLearnMore: 'Learn more',
braveWalletAuroraModalOPenButtonText: 'Open rainbowbridge.app'
})
@@ -31,3 +31,34 @@ export const getRampAssetSymbol = (asset: BraveWallet.BlockchainToken) => {
const rampNetworkPrefix = getRampNetworkPrefix(asset.chainId)
return rampNetworkPrefix !== '' ? `${rampNetworkPrefix}_${asset.symbol.toUpperCase()}` : asset.symbol
}
export const auroraSupportedContractAddresses = [
'0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9', // AAVE
'0xaaaaaa20d9e0e2461697782ef11675f668207961', // AURORA
'0xba100000625a3754423978a60c9317c58a424e3d', // BAL
'0x0d8775f648430679a709e98d2b0cb6250d2887ef', // BAT
'0xc00e94cb662c3520282e6f5717214004a7f26888', // COMP
'0x2ba592f78db6436527729929aaf6c908497cb200', // CREAM
'0x6b175474e89094c44da98b954eedeac495271d0f', // DAI
'0x43dfc4159d86f3a37a5a4b3d4580b888ad7d4ddd', // DODO
'0x3ea8ea4237344c9931214796d9417af1a1180770', // FLX
'0x853d955acef822db058eb8505911ed77f175b99e', // FRAX
'0x3432b6a60d23ca0dfca7761b7ab56459d9c964d0', // FXS
'0xd9c2d319cd7e6177336b0a9c93c21cb48d84fb54', // HAPI
'0x514910771af9ca656af840dff83e8264ecf986ca', // LINK
'0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', // MKR
'0x1117ac6ad6cdf1a3bc543bad3b133724620522d5', // MODA
'0xf5cfbc74057c610c8ef151a439252680ac68c6dc', // OCT
'0x9aeb50f542050172359a0e1a25a9933bc8c01259', // OIN
'0xea7cc765ebc94c4805e3bff28d7e4ae48d06468a', // PAD
'0x429881672b9ae42b8eba0e26cd9c73711b891ca5', // PICKLE
'0x408e41876cccdc0f92210600ef50372656052a38', // REN
'0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f', // SNX
'0x6b3595068778dd592e39a122f4f5a5cf09c90fe2', // SUSHI
'0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', // UNI
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC
'0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT
'0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', // WBTC
'0x4691937a7508860f876c9c0a2a617e7d9e945d4b', // WOO
'0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e' // YFI
].map(contractAddress => contractAddress.toLowerCase())
+4
View File
@@ -512,4 +512,8 @@
<message name="IDS_BRAVE_WALLET_HELP_CENTER_TEXT" desc="Help Center text description below buy-send-swap">Need help? See</message>
<message name="IDS_BRAVE_WALLET_HARDWARE_OPERATION_UNSUPPORTED_ERROR" desc="Error message of unsupported hardware wallet operation">This hardware wallet does not support this operation.</message>
<message name="IDS_BRAVE_WALLET_ENTER_YOUR_PASSWORD" desc="Prompt for the user to enter their password">Enter your Brave Wallet password</message>
<message name="IDS_BRAVE_WALLET_AURORA_MODAL_TITLE" desc="Bridge to Aurora modal title">Open rainbowbridge.app?</message>
<message name="IDS_BRAVE_WALLET_AURORA_MODAL_DESCRIPTION" desc="Bridge to Aurora modal description">Bridging assets across networks allows you to use your crypto on other networks and other DApp ecosystems. Bridging assets to other networks has some risks.</message>
<message name="IDS_BRAVE_WALLET_AURORA_MODAL_LEARN_MORE" desc="Bridge to Aurora modal learn more link text">Learn more</message>
<message name="IDS_BRAVE_WALLET_AURORA_MODAL_OPEN_BUTTON_TEXT" desc="Bridge to Aurora modal action button text">Open rainbowbridge.app</message>
</grit-part>