Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [7.73.2]

### Added

- Added Polymarket CLOB v2 support (#29076)

### Fixed

- Fixed Perps $0 balance display for accounts funded via HyperLiquid spot USDC (#29110)
- Fixed Perps balance not refreshing after trades, funding, or transfers for HyperLiquid users, and corrected total balance inflation on Unified-mode accounts (#29226)

## [7.73.1]

### Fixed
Expand Down Expand Up @@ -11222,7 +11233,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#957](https://github.com/MetaMask/metamask-mobile/pull/957): fix timeouts (#957)
- [#954](https://github.com/MetaMask/metamask-mobile/pull/954): Bugfix: onboarding navigation (#954)

[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.73.1...HEAD
[Unreleased]: https://github.com/MetaMask/metamask-mobile/compare/v7.73.2...HEAD
[7.73.2]: https://github.com/MetaMask/metamask-mobile/compare/v7.73.1...v7.73.2
[7.73.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.73.0...v7.73.1
[7.73.0]: https://github.com/MetaMask/metamask-mobile/compare/v7.72.1...v7.73.0
[7.72.1]: https://github.com/MetaMask/metamask-mobile/compare/v7.72.0...v7.72.1
Expand Down
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ android {
applicationId "io.metamask"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionName "7.73.0"
versionCode 4594
versionName "7.73.2"
versionCode 4647
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
manifestPlaceholders.MM_BRANCH_KEY_TEST = "$System.env.MM_BRANCH_KEY_TEST"
Expand Down
6 changes: 6 additions & 0 deletions app/components/UI/Perps/Perps.testIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,12 @@ export const PerpsWithdrawViewSelectorsIDs = {
DEST_TOKEN_AREA: 'dest-token-area',
CONTINUE_BUTTON: 'continue-button',
BOTTOM_SHEET_TOOLTIP: 'withdraw-bottom-sheet-tooltip',
RECEIVE_VALUE: 'perps-withdraw-receive-value',
FEE_VALUE: 'perps-withdraw-fee-value',
TIME_VALUE: 'perps-withdraw-time-value',
// Must render availableBalance only (not availableToTradeBalance):
// withdraw does not offer spot collateral.
AVAILABLE_BALANCE_TEXT: 'perps-withdraw-available-balance-text',
};

// ========================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,47 @@ describe('PerpsMarketDetailsView', () => {
).toBeNull();
});

it('shows add funds CTA when total balance is funded but spendable balance has no direct order path', () => {
mockUseDefaultPayWithTokenWhenNoPerpsBalance.mockReturnValue(null);
mockUsePerpsAccount.mockReturnValue({
account: {
availableBalance: '0.00',
marginUsed: '0.00',
unrealizedPnl: '0.00',
returnOnEquity: '0.00',
totalBalance: '100.00',
},
isInitialLoading: false,
});
mockUsePerpsLiveAccount.mockReturnValue({
account: {
availableBalance: '0',
marginUsed: '0',
unrealizedPnl: '0',
returnOnEquity: '0',
totalBalance: '100',
},
isInitialLoading: false,
});

const { getByTestId, queryByTestId } = renderWithProvider(
<PerpsConnectionProvider>
<PerpsMarketDetailsView />
</PerpsConnectionProvider>,
{ state: initialState },
);

expect(
getByTestId(PerpsMarketDetailsViewSelectorsIDs.ADD_FUNDS_BUTTON),
).toBeOnTheScreen();
expect(
queryByTestId(PerpsMarketDetailsViewSelectorsIDs.LONG_BUTTON),
).toBeNull();
expect(
queryByTestId(PerpsMarketDetailsViewSelectorsIDs.SHORT_BUTTON),
).toBeNull();
});

it('calls navigateToConfirmation and depositWithConfirmation when add funds is pressed', async () => {
mockUseDefaultPayWithTokenWhenNoPerpsBalance.mockReturnValue(null);
mockUsePerpsAccount.mockReturnValue({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -429,17 +429,15 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
useDefaultPayWithTokenWhenNoPerpsBalance();
const { depositWithConfirmation } = usePerpsTrading();
const { navigateToConfirmation } = useConfirmNavigation();
const availableBalance = Number.parseFloat(
account?.availableBalance?.toString() ?? '0',
const tradeableBalance = Number.parseFloat(
account?.availableToTradeBalance?.toString() ??
account?.availableBalance?.toString() ??
'0',
);
const showAddFundsCTA =
isEligible &&
!isLoadingPosition &&
!existingPosition &&
!isAtOICap &&
const hasDirectOrderFundingPath =
!isLoadingAccount &&
availableBalance < PERPS_MIN_BALANCE_THRESHOLD &&
defaultPayTokenWhenNoPerpsBalance === null;
(tradeableBalance >= PERPS_MIN_BALANCE_THRESHOLD ||
defaultPayTokenWhenNoPerpsBalance !== null);

const handleAddFunds = useCallback(async () => {
if (!isEligible) {
Expand Down Expand Up @@ -1151,9 +1149,13 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
const shouldShowNewPositionActions =
hasLongShortButtons && !existingPosition && !isAtOICap;
const shouldShowAddFundsCTASection =
shouldShowNewPositionActions && showAddFundsCTA;
shouldShowNewPositionActions &&
isEligible &&
!isLoadingAccount &&
!isLoadingPosition &&
!hasDirectOrderFundingPath;
const shouldShowLongShortButtonsOnly =
shouldShowNewPositionActions && !showAddFundsCTA;
shouldShowNewPositionActions && !shouldShowAddFundsCTASection;

const shouldShowPerpsMarketInsights =
isPerpsInsightsEnabled &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,11 @@ const PerpsWithdrawView: React.FC = () => {
]}
/>
</Box>
<Text variant={TextVariant.BodyMD} color={TextColor.Alternative}>
<Text
variant={TextVariant.BodyMD}
color={TextColor.Alternative}
testID={PerpsWithdrawViewSelectorsIDs.AVAILABLE_BALANCE_TEXT}
>
{strings('perps.withdrawal.available_balance', {
amount: formattedBalance,
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,13 @@ const PerpsMarketBalanceActions: React.FC<PerpsMarketBalanceActionsProps> = ({
[stopBalanceAnimation],
);

const availableBalance = perpsAccount?.availableBalance || '0';
// Order-entry surface reads availableToTradeBalance (withdrawable +
// unreserved spot collateral). Withdraw surfaces keep reading
// availableBalance directly.
const availableBalance =
perpsAccount?.availableToTradeBalance ??
perpsAccount?.availableBalance ??
'0';

// Show skeleton while loading initial account data
if (isInitialLoading) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ export function useDefaultPayWithTokenWhenNoPerpsBalance(): PerpsSelectedPayment
if (!featureEnabled) {
return null;
}
const availableBalance = Number.parseFloat(
perpsAccount?.availableBalance?.toString() ?? '0',
const tradeableBalance = Number.parseFloat(
perpsAccount?.availableToTradeBalance?.toString() ??
perpsAccount?.availableBalance?.toString() ??
'0',
);

if (availableBalance > PERPS_MIN_BALANCE_THRESHOLD) {
if (tradeableBalance > PERPS_MIN_BALANCE_THRESHOLD) {
return null;
}
if (!allowlistAssets?.length) {
Expand Down Expand Up @@ -93,6 +95,7 @@ export function useDefaultPayWithTokenWhenNoPerpsBalance(): PerpsSelectedPayment
}, [
featureEnabled,
perpsAccount?.availableBalance,
perpsAccount?.availableToTradeBalance,
allowlistAssets,
activeProvider,
currentNetwork,
Expand Down
4 changes: 3 additions & 1 deletion app/components/UI/Perps/hooks/usePerpsOrderForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ export function usePerpsOrderForm(
const availableBalance = Number.parseFloat(
effectiveAvailableBalanceParam != null
? effectiveAvailableBalanceParam.toString()
: (account?.availableBalance?.toString() ?? '0'),
: (account?.availableToTradeBalance?.toString() ??
account?.availableBalance?.toString() ??
'0'),
);

// When paying with a custom token, use selected token amount in USD (including 0); otherwise use Perps balance
Expand Down
3 changes: 3 additions & 0 deletions app/components/UI/Perps/utils/hyperLiquidAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,7 @@ describe('hyperLiquidAdapter', () => {

expect(result).toEqual({
availableBalance: '700.25',
availableToTradeBalance: '700.25', // withdrawable + free spot (no spot provided)
marginUsed: '300.25',
unrealizedPnl: '24.5', // 50.0 + (-25.5)
returnOnEquity: '7.991673605328893', // Calculated from weighted return and margin
Expand Down Expand Up @@ -1120,6 +1121,7 @@ describe('hyperLiquidAdapter', () => {

expect(result).toEqual({
availableBalance: '350.0',
availableToTradeBalance: '850.5', // withdrawable 350 + free spot 500.5 (hold = 0)
marginUsed: '150.0',
unrealizedPnl: '100',
returnOnEquity: '0',
Expand Down Expand Up @@ -1158,6 +1160,7 @@ describe('hyperLiquidAdapter', () => {

expect(result).toEqual({
availableBalance: '800.0',
availableToTradeBalance: '800', // withdrawable + 0 (no spot totals)
marginUsed: '200.0',
unrealizedPnl: '0',
returnOnEquity: '0',
Expand Down
20 changes: 20 additions & 0 deletions app/components/UI/Predict/controllers/PredictController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import { filterSupportedLeagues } from '../constants/sports';
import { PREDICT_BALANCE_PLACEHOLDER_ADDRESS } from '../constants/transactions';
import { PolymarketProvider } from '../providers/polymarket/PolymarketProvider';
import {
LEGACY_V2_CLOB_BASE_URL,
MATIC_CONTRACTS,
POLYMARKET_PROVIDER_ID,
} from '../providers/polymarket/constants';
Expand Down Expand Up @@ -520,12 +521,31 @@ export class PredictController extends BaseController<
),
) ?? false;

const predictClobV2Enabled =
validatedVersionGatedFeatureFlag(
unwrapRemoteFeatureFlag<VersionGatedFeatureFlag>(flags.predictClobV2),
) ?? false;

const predictClobV2UseLegacyClobHost = predictClobV2Enabled
? (validatedVersionGatedFeatureFlag(
unwrapRemoteFeatureFlag<VersionGatedFeatureFlag>(
flags.predictClobV2UseLegacyClobHost,
),
) ?? false)
: false;

const predictClobV2ClobBaseUrl = predictClobV2UseLegacyClobHost
? LEGACY_V2_CLOB_BASE_URL
: undefined;

return {
feeCollection,
liveSportsLeagues,
marketHighlightsFlag,
fakOrdersEnabled,
predictWithAnyTokenEnabled,
predictClobV2Enabled,
predictClobV2ClobBaseUrl,
};
}

Expand Down
Loading
Loading