Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ramps): update isNativeTokenBuyable to include BTC #25621

Merged
merged 20 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4253ead
feat(ramps): add is-rampable logic to btc
georgeweiler Jul 2, 2024
79b1c68
chore: removes hardcoded dev values
georgeweiler Jul 2, 2024
91e7e7d
refactor: use numeric util for zero evaluation
georgeweiler Jul 2, 2024
5edaede
Merge branch 'develop' into feature/btc-is-token-buyable
georgeweiler Jul 2, 2024
f8d2115
test: adds ramp test to btc-overview
georgeweiler Jul 2, 2024
081d6a3
test: add test for multichain utils
georgeweiler Jul 2, 2024
641a0b1
Merge branch 'feature/btc-is-token-buyable' of https://github.com/Met…
georgeweiler Jul 2, 2024
78d33b4
Merge branch 'develop' of https://github.com/MetaMask/metamask-extens…
georgeweiler Jul 2, 2024
947bbf0
feat: adds btc variant to asset list
georgeweiler Jul 2, 2024
a29ac79
test: fix broken ramps slice test
georgeweiler Jul 3, 2024
653de69
test: adds tests for ramps slice btc logic and selectors
georgeweiler Jul 3, 2024
496cd23
test: removes unneeded code in ramp slice test
georgeweiler Jul 3, 2024
b7015e2
Merge branch 'develop' of https://github.com/MetaMask/metamask-extens…
georgeweiler Jul 3, 2024
91cff94
test: fixes eth-overview test for disabled buy button
georgeweiler Jul 3, 2024
75c8f5b
add conditional compilation comments for build configurations
georgeweiler Jul 3, 2024
7c61b61
Merge branch 'develop' into feature/btc-is-token-buyable
georgeweiler Jul 3, 2024
f5b6a78
chore: some small fixes from code review
georgeweiler Jul 3, 2024
dfccd87
Merge branch 'feature/btc-is-token-buyable' of https://github.com/Met…
georgeweiler Jul 3, 2024
785472f
add conditional compilation comments for build configurations
georgeweiler Jul 3, 2024
1534cf9
Merge branch 'develop' into feature/btc-is-token-buyable
georgeweiler Jul 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions ui/components/app/asset-list/asset-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import {
getMultichainCurrencyImage,
getMultichainIsMainnet,
getMultichainSelectedAccountCachedBalance,
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
getMultichainIsBitcoin,
///: END:ONLY_INCLUDE_IF
getMultichainSelectedAccountCachedBalanceIsZero,
} from '../../../selectors/multichain';
import { useCurrencyDisplay } from '../../../hooks/useCurrencyDisplay';
import { MetaMetricsContext } from '../../../contexts/metametrics';
Expand Down Expand Up @@ -98,24 +102,33 @@ const AssetList = ({ onClickAsset, showTokensLinks }) => {
getIstokenDetectionInactiveOnNonMainnetSupportedNetwork,
);

const { tokensWithBalances, totalFiatBalance, loading } =
useAccountTotalFiatBalance(selectedAccount, shouldHideZeroBalanceTokens);
const { tokensWithBalances, loading } = useAccountTotalFiatBalance(
selectedAccount,
shouldHideZeroBalanceTokens,
);
tokensWithBalances.forEach((token) => {
// token.string is the balance displayed in the TokenList UI
token.string = roundToDecimalPlacesRemovingExtraZeroes(token.string, 5);
});
const balanceIsZero = Number(totalFiatBalance) === 0;

const balanceIsZero = useSelector(
getMultichainSelectedAccountCachedBalanceIsZero,
);

///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
const isBuyableChain = useSelector(getIsNativeTokenBuyable);
const shouldShowBuy = isBuyableChain && balanceIsZero;
///: END:ONLY_INCLUDE_IF

const isEvm = useSelector(getMultichainIsEvm);

// NOTE: Since we can parametrize it now, we keep the original behavior
// for EVM assets
const shouldShowTokensLinks = showTokensLinks ?? isEvm;

///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
const isBtc = useSelector(getMultichainIsBitcoin);
///: END:ONLY_INCLUDE_IF

let isStakeable = isMainnet && isEvm;
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
isStakeable = false;
Expand All @@ -133,7 +146,13 @@ const AssetList = ({ onClickAsset, showTokensLinks }) => {
{
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
shouldShowBuy ? (
<RampsCard variant={RAMPS_CARD_VARIANT_TYPES.TOKEN} />
<RampsCard
variant={
isBtc
? RAMPS_CARD_VARIANT_TYPES.BTC
: RAMPS_CARD_VARIANT_TYPES.TOKEN
}
/>
) : null
///: END:ONLY_INCLUDE_IF
}
Expand Down
136 changes: 98 additions & 38 deletions ui/components/app/wallet-overview/btc-overview.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import mockState from '../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../test/jest/rendering';
import { MultichainNetworks } from '../../../../shared/constants/multichain/networks';
import { RampsMetaMaskEntry } from '../../../hooks/ramps/useRamps/useRamps';
import { defaultBuyableChains } from '../../../ducks/ramps/constants';
import BtcOverview from './btc-overview';

const PORTOFOLIO_URL = 'https://portfolio.test';
Expand Down Expand Up @@ -40,43 +41,63 @@ const mockNonEvmAccount = {
type: BtcAccountType.P2wpkh,
};

function getStore(state?: Record<string, unknown>) {
return configureMockStore([thunk])({
metamask: {
...mockState.metamask,
internalAccounts: {
accounts: {
[mockNonEvmAccount.id]: mockNonEvmAccount,
},
selectedAccount: mockNonEvmAccount.id,
},
// (Multichain) BalancesController
balances: {
[mockNonEvmAccount.id]: {
[MultichainNativeAssets.BITCOIN]: {
amount: mockNonEvmBalance,
unit: 'BTC',
},
},
},
// (Multichain) RatesController
fiatCurrency: 'usd',
rates: {
[Cryptocurrency.Btc]: {
conversionRate: '1.000',
conversionDate: 0,
},
const mockBtcChain = {
active: true,
chainId: MultichainNetworks.BITCOIN,
chainName: 'Bitcoin',
shortName: 'Bitcoin',
nativeTokenSupported: true,
isEvm: false,
};
// default chains do not include BTC
const mockBuyableChainsWithoutBtc = defaultBuyableChains.filter(
(chain) => chain.chainId !== MultichainNetworks.BITCOIN,
);
const mockBuyableChainsWithBtc = [...mockBuyableChainsWithoutBtc, mockBtcChain];

const mockMetamaskStore = {
...mockState.metamask,
internalAccounts: {
accounts: {
[mockNonEvmAccount.id]: mockNonEvmAccount,
},
selectedAccount: mockNonEvmAccount.id,
},
// (Multichain) BalancesController
balances: {
[mockNonEvmAccount.id]: {
[MultichainNativeAssets.BITCOIN]: {
amount: mockNonEvmBalance,
unit: 'BTC',
},
cryptocurrencies: [Cryptocurrency.Btc],
// Required, during onboarding, the extension will assume we're in an "EVM context", meaning
// most multichain selectors will not use non-EVM logic despite having a non-EVM
// selected account
completedOnboarding: true,
// Used when clicking on some buttons
metaMetricsId: mockMetaMetricsId,
// Override state if provided
...state,
},
},
// (Multichain) RatesController
fiatCurrency: 'usd',
rates: {
[Cryptocurrency.Btc]: {
conversionRate: '1.000',
conversionDate: 0,
},
},
cryptocurrencies: [Cryptocurrency.Btc],
// Required, during onboarding, the extension will assume we're in an "EVM context", meaning
// most multichain selectors will not use non-EVM logic despite having a non-EVM
// selected account
completedOnboarding: true,
// Used when clicking on some buttons
metaMetricsId: mockMetaMetricsId,
// Override state if provided
};
const mockRampsStore = {
buyableChains: mockBuyableChainsWithoutBtc,
};

function getStore(state?: Record<string, unknown>) {
return configureMockStore([thunk])({
metamask: mockMetamaskStore,
ramps: mockRampsStore,
...state,
});
}

Expand All @@ -103,8 +124,11 @@ describe('BtcOverview', () => {
const { container } = renderWithProvider(
<BtcOverview />,
getStore({
// The balances won't be available
balances: {},
metamask: {
...mockMetamaskStore,
// The balances won't be available
balances: {},
},
}),
);

Expand Down Expand Up @@ -134,8 +158,44 @@ describe('BtcOverview', () => {
expect(buyButton).toBeInTheDocument();
});

it('opens the Portfolio "Buy & Sell" URI when clicking on "Buy & Sell" button', async () => {
it('"Buy & Sell" button is disabled if BTC is not buyable', () => {
const { queryByTestId } = renderWithProvider(<BtcOverview />, getStore());
const buyButton = queryByTestId(BTC_OVERVIEW_BUY);

expect(buyButton).toBeInTheDocument();
expect(buyButton).toBeDisabled();
});

it('"Buy & Sell" button is enabled if BTC is buyable', () => {
const storeWithBtcBuyable = getStore({
ramps: {
buyableChains: mockBuyableChainsWithBtc,
},
});

const { queryByTestId } = renderWithProvider(
<BtcOverview />,
storeWithBtcBuyable,
);

const buyButton = queryByTestId(BTC_OVERVIEW_BUY);

expect(buyButton).toBeInTheDocument();
expect(buyButton).not.toBeDisabled();
});

it('opens the Portfolio "Buy & Sell" URI when clicking on "Buy & Sell" button', async () => {
const storeWithBtcBuyable = getStore({
ramps: {
buyableChains: mockBuyableChainsWithBtc,
},
});

const { queryByTestId } = renderWithProvider(
<BtcOverview />,
storeWithBtcBuyable,
);

const openTabSpy = jest.spyOn(global.platform, 'openTab');

const buyButton = queryByTestId(BTC_OVERVIEW_BUY);
Expand Down
12 changes: 7 additions & 5 deletions ui/components/app/wallet-overview/btc-overview.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react';

import { useSelector } from 'react-redux';
import {
getMultichainProviderConfig,
getMultichainSelectedAccountCachedBalance,
} from '../../../selectors/multichain';
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
import { getIsBitcoinBuyable } from '../../../ducks/ramps';
///: END:ONLY_INCLUDE_IF
import { CoinOverview } from './coin-overview';

type BtcOverviewProps = {
Expand All @@ -14,6 +16,9 @@ type BtcOverviewProps = {
const BtcOverview = ({ className }: BtcOverviewProps) => {
const { chainId } = useSelector(getMultichainProviderConfig);
const balance = useSelector(getMultichainSelectedAccountCachedBalance);
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
const isBtcBuyable = useSelector(getIsBitcoinBuyable);
///: END:ONLY_INCLUDE_IF

return (
<CoinOverview
Expand All @@ -25,10 +30,7 @@ const BtcOverview = ({ className }: BtcOverviewProps) => {
isSwapsChain={false}
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
isBridgeChain={false}
isBuyableChain
// TODO: Remove this logic once `isNativeTokenBuyable` has been
// merged (see: https://github.com/MetaMask/metamask-extension/pull/24041)
isBuyableChainWithoutSigning
isBuyableChain={isBtcBuyable}
///: END:ONLY_INCLUDE_IF
/>
);
Expand Down
13 changes: 1 addition & 12 deletions ui/components/app/wallet-overview/coin-buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,6 @@ const CoinButtons = ({
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
isBridgeChain,
isBuyableChain,
// TODO: Remove this logic once `isNativeTokenBuyable` has been
// merged (see: https://github.com/MetaMask/metamask-extension/pull/24041)
isBuyableChainWithoutSigning = false,
defaultSwapsToken,
///: END:ONLY_INCLUDE_IF
classPrefix = 'coin',
Expand All @@ -88,7 +85,6 @@ const CoinButtons = ({
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
isBridgeChain: boolean;
isBuyableChain: boolean;
isBuyableChainWithoutSigning?: boolean;
defaultSwapsToken?: SwapsEthToken;
///: END:ONLY_INCLUDE_IF
classPrefix?: string;
Expand All @@ -112,10 +108,6 @@ const CoinButtons = ({
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
{ condition: !isBuyableChain, message: '' },
///: END:ONLY_INCLUDE_IF
{
condition: !(isSigningEnabled || isBuyableChainWithoutSigning),
message: 'methodNotSupported',
},
],
sendButton: [
{ condition: !isSigningEnabled, message: 'methodNotSupported' },
Expand Down Expand Up @@ -339,10 +331,7 @@ const CoinButtons = ({
Icon={
<Icon name={IconName.PlusMinus} color={IconColor.primaryInverse} />
}
disabled={
!isBuyableChain ||
!(isSigningEnabled || isBuyableChainWithoutSigning)
}
disabled={!isBuyableChain}
data-testid={`${classPrefix}-overview-buy`}
label={t('buyAndSell')}
onClick={handleBuyAndSellOnClick}
Expand Down
3 changes: 0 additions & 3 deletions ui/components/app/wallet-overview/coin-overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export type CoinOverviewProps = {
defaultSwapsToken?: SwapsEthToken;
isBridgeChain: boolean;
isBuyableChain: boolean;
isBuyableChainWithoutSigning: boolean;
///: END:ONLY_INCLUDE_IF
isSwapsChain: boolean;
isSigningEnabled: boolean;
Expand All @@ -55,7 +54,6 @@ export const CoinOverview = ({
defaultSwapsToken,
isBridgeChain,
isBuyableChain,
isBuyableChainWithoutSigning,
///: END:ONLY_INCLUDE_IF
isSwapsChain,
isSigningEnabled,
Expand Down Expand Up @@ -152,7 +150,6 @@ export const CoinOverview = ({
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
isBridgeChain,
isBuyableChain,
isBuyableChainWithoutSigning,
defaultSwapsToken,
///: END:ONLY_INCLUDE_IF
classPrefix,
Expand Down
1 change: 0 additions & 1 deletion ui/components/app/wallet-overview/eth-overview.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,6 @@ describe('EthOverview', () => {

describe('Disabled buttons when an account cannot sign transactions', () => {
const buttonTestCases = [
{ testId: ETH_OVERVIEW_BUY, buttonText: 'Buy & Sell' },
{ testId: ETH_OVERVIEW_SEND, buttonText: 'Send' },
{ testId: ETH_OVERVIEW_SWAP, buttonText: 'Swap' },
{ testId: ETH_OVERVIEW_BRIDGE, buttonText: 'Bridge' },
Expand Down
Loading
Loading