Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
12 changes: 6 additions & 6 deletions apps/legacy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
"typecheck": "yarn tsc --skipLibCheck --noEmit"
},
"dependencies": {
"@avalabs/avalanche-module": "1.9.10",
"@avalabs/avalanche-module": "0.0.0-fix-nft-set-approval-for-all-20250829175304",
"@avalabs/avalanchejs": "5.1.0-alpha.2",
"@avalabs/bitcoin-module": "1.9.10",
"@avalabs/bitcoin-module": "0.0.0-fix-nft-set-approval-for-all-20250829175304",
"@avalabs/bridge-unified": "4.0.3",
"@avalabs/core-bridge-sdk": "3.1.0-alpha.60",
"@avalabs/core-chains-sdk": "3.1.0-alpha.60",
Expand All @@ -30,13 +30,13 @@
"@avalabs/core-token-prices-sdk": "3.1.0-alpha.60",
"@avalabs/core-utils-sdk": "3.1.0-alpha.60",
"@avalabs/core-wallets-sdk": "3.1.0-alpha.60",
"@avalabs/evm-module": "1.9.10",
"@avalabs/evm-module": "0.0.0-fix-nft-set-approval-for-all-20250829175304",
"@avalabs/glacier-sdk": "3.1.0-alpha.60",
"@avalabs/hvm-module": "1.9.10",
"@avalabs/hvm-module": "0.0.0-fix-nft-set-approval-for-all-20250829175304",
"@avalabs/hw-app-avalanche": "0.14.1",
"@avalabs/svm-module": "1.9.10",
"@avalabs/svm-module": "0.0.0-fix-nft-set-approval-for-all-20250829175304",
"@avalabs/types": "3.1.0-alpha.60",
"@avalabs/vm-module-types": "1.9.10",
"@avalabs/vm-module-types": "0.0.0-fix-nft-set-approval-for-all-20250829175304",
"@blockaid/client": "0.10.0",
"@coinbase/cbpay-js": "1.6.0",
"@core/common": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion apps/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@avalabs/core-wallets-sdk": "3.1.0-alpha.60",
"@avalabs/k2-alpine": "1.228.0",
"@avalabs/types": "3.1.0-alpha.60",
"@avalabs/vm-module-types": "1.9.10",
"@avalabs/vm-module-types": "0.0.0-fix-nft-set-approval-for-all-20250829175304",
"@core/common": "workspace:*",
"@core/messaging": "workspace:*",
"@core/service-worker": "workspace:*",
Expand Down
8 changes: 8 additions & 0 deletions apps/next/src/localization/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"Anyone with this private key can access the account(s) associated with it": "Anyone with this private key can access the account(s) associated with it",
"Approval transaction failed": "Approval transaction failed",
"Approve": "Approve",
"Approves all {{token}}": "Approves all {{token}}",
"Approving will give this dApp access to your active account.": "Approving will give this dApp access to your active account.",
"Are you sure that you want to cancel this request?": "Are you sure that you want to cancel this request?",
"Are you sure you want to delete selected accounts?": "Are you sure you want to delete selected accounts?",
Expand Down Expand Up @@ -133,6 +134,7 @@
"Dark": "Dark",
"Decide what default view works best for you, either a floating interface or a sidebar docked to the side to show more content": "Decide what default view works best for you, either a floating interface or a sidebar docked to the side to show more content",
"Decrypt Recovery Phrase": "Decrypt Recovery Phrase",
"Default": "Default",
"Delete": "Delete",
"Delete selected": "Delete selected",
"Deleting these accounts is permanent and cannot be undone": "Deleting these accounts is permanent and cannot be undone",
Expand All @@ -148,6 +150,7 @@
"Download Ledger Live to update": "Download Ledger Live to update",
"Drop your file here to upload": "Drop your file here to upload",
"Edit network fee": "Edit network fee",
"Edit spend limit": "Edit spend limit",
"Email address": "Email address",
"Enable a sandbox environment for testing without using real funds": "Enable a sandbox environment for testing without using real funds",
"English": "English",
Expand Down Expand Up @@ -423,6 +426,7 @@
"Send to": "Send to",
"Sending this token is not supported yet.": "Sending this token is not supported yet.",
"Sending this type of token is not supported by Core": "Sending this type of token is not supported by Core",
"Set a limit that you will allow to automatically spend.": "Set a limit that you will allow to automatically spend.",
"Settings": "Settings",
"Show me Trending Tokens": "Show me Trending Tokens",
"Show password": "Show password",
Expand All @@ -440,6 +444,7 @@
"Something went wrong. Please try again.": "Something went wrong. Please try again.",
"Sort": "Sort",
"Spanish": "Spanish",
"Spend limit": "Spend limit",
"Staked": "Staked",
"Stay updated on latest airdrops, events and more! You can unsubscribe anytime. For more details, see our <policyLink>Privacy Policy</policyLink>": "Stay updated on latest airdrops, events and more! You can unsubscribe anytime. For more details, see our <policyLink>Privacy Policy</policyLink>",
"Storage update failed": "Storage update failed",
Expand Down Expand Up @@ -474,6 +479,7 @@
"This asset cannot be bridged": "This asset cannot be bridged",
"This email looks invalid": "This email looks invalid",
"This file requires a password. This password was set when the file was created.": "This file requires a password. This password was set when the file was created.",
"This is a placeholder screen just to allow us to test the dApp interactions.": "This is a placeholder screen just to allow us to test the dApp interactions.",
"This is taking longer than expected. Please try again later.": "This is taking longer than expected. Please try again later.",
"This key gives access to your account’s addresses": "This key gives access to your account’s addresses",
"This operation requires {{total}} approvals.": "This operation requires {{total}} approvals.",
Expand Down Expand Up @@ -530,6 +536,8 @@
"Unknown network": "Unknown network",
"Unknown network fee": "Unknown network fee",
"Unknown transaction error": "Unknown transaction error",
"Unlimited": "Unlimited",
"Unlimited {{currency}}": "Unlimited {{currency}}",
"Unlock airdrops": "Unlock airdrops",
"Unnamed FIDO Device": "Unnamed FIDO Device",
"Unsupporetd secret type": "Unsupporetd secret type",
Expand Down
52 changes: 52 additions & 0 deletions apps/next/src/pages/Approve/ExtensionActionApprovalScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useCallback } from 'react';
import { Typography } from '@avalabs/k2-alpine';
import { useTranslation } from 'react-i18next';

import { ActionStatus } from '@core/types';
import { useApproveAction, useGetRequestId } from '@core/ui';

import {
ActionDrawer,
ApprovalScreenTitle,
LoadingScreen,
Styled,
} from './components';

export const ExtensionActionApprovalScreen = () => {
const { t } = useTranslation();

const requestId = useGetRequestId();
const { action, updateAction, cancelHandler } = useApproveAction(requestId);

const approve = useCallback(async () => {
updateAction({
status: ActionStatus.SUBMITTING,
id: requestId,
});
}, [updateAction, requestId]);

if (!action) {
return <LoadingScreen />;
}

return (
<Styled.ApprovalScreenPage>
<Styled.NoScrollStack>
<ApprovalScreenTitle
title={t('🚧 Placeholder - needs implementation 🚧')}
/>
<Typography variant="body3">
{t(
'This is a placeholder screen just to allow us to reject/approve some wallet-specific interactions (i.e. chain switching).',
)}
</Typography>
</Styled.NoScrollStack>
<ActionDrawer
open
approve={approve}
reject={cancelHandler}
action={action}
/>
</Styled.ApprovalScreenPage>
);
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Stack } from '@avalabs/k2-alpine';
import { DisplayData } from '@avalabs/vm-module-types';

import { EvmNetwork } from '@core/types';
import { Action, EnsureDefined, EvmNetwork } from '@core/types';

import { ActionDetailsProps } from '../../../types';
import { DetailsSection } from '../generic/DetailsSection';
import { DetailsItem } from '../generic/DetailsItem';
import { TransactionBalanceChange } from '../generic/TransactionBalanceChange/TransactionBalanceChange';
import { EvmNetworkFeeWidget } from './EvmNetworkFeeWidget/EvmNetworkFeeWidget';
import { DetailsSection } from '../generic/DetailsSection';
import { TransactionBalanceChange } from '../generic/TransactionBalanceChange';

import { EvmTokenApprovals } from './EvmTokenApprovals';
import { EvmNetworkFeeWidget } from './EvmNetworkFeeWidget';

type EvmActionDetailsProps = Omit<ActionDetailsProps, 'network'> & {
network: EvmNetwork;
Expand All @@ -25,6 +28,7 @@ export const EvmActionDetails = ({
isSimulationSuccessful={action.displayData.isSimulationSuccessful}
/>
)}
{hasTokenApprovals(action) && <EvmTokenApprovals action={action} />}
{action.displayData.details.map((section) => (
<DetailsSection key={section.title}>
{section.items.map((item, index) => (
Expand All @@ -38,3 +42,9 @@ export const EvmActionDetails = ({
</Stack>
);
};

const hasTokenApprovals = (
action: Action<DisplayData>,
): action is Action<EnsureDefined<DisplayData, 'tokenApprovals'>> => {
return action.displayData.tokenApprovals !== undefined;
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';

import { useConnectionContext } from '@core/ui';
import { ExtensionRequest, FeeRate } from '@core/types';
Expand All @@ -10,7 +10,7 @@ import { useUpdateAccountBalance } from '@/hooks/useUpdateAccountBalance';
import { useCurrentFeesForNetwork } from '@/hooks/useCurrentFeesForNetwork';

import { EvmFeePreset } from '../../types';
import { getFeeInfo, hasEnoughForFee } from './lib';
import { getFeeInfo, getInitialFeeRate, hasEnoughForFee } from './lib';
import { EvmTxSigningData, UseEvmTransactionFee } from './types';

export const useEvmTransactionFee: UseEvmTransactionFee = ({
Expand Down Expand Up @@ -103,6 +103,17 @@ export const useEvmTransactionFee: UseEvmTransactionFee = ({
[networkFee, updateFee],
);

const initialFeeRate = useRef<bigint | undefined>(
getInitialFeeRate(signingData),
);

useEffect(() => {
// If the dapp did not vie us any fee rate, we must initialize it ourselves.
if (!initialFeeRate.current) {
choosePreset('fast');
}
}, [networkFee, choosePreset]);

if (!networkFee || !nativeToken || !signingData || !customPreset) {
return {
isLoading: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Stack } from '@avalabs/k2-alpine';
import { FC, useRef } from 'react';
import { DisplayData } from '@avalabs/vm-module-types';

import { Action, EnsureDefined } from '@core/types';
import { useBalancesContext } from '@core/ui';

import { DetailsSection } from '../../generic/DetailsSection';
import { getApprovalValue, isUnlimitedApproval } from './lib';
import { CustomApprovalLimit, TokenSpendLimitCard } from './components';

type EvmTokenApprovalsProps = {
action: Action<EnsureDefined<DisplayData, 'tokenApprovals'>>;
};

export const EvmTokenApprovals: FC<EvmTokenApprovalsProps> = ({ action }) => {
const { getTokenPrice } = useBalancesContext();

const { tokenApprovals } = action.displayData;

const requestedApprovalRef = useRef(tokenApprovals.approvals[0]);
const hasOnlyOneEditableApproval =
tokenApprovals.approvals.length === 1 && tokenApprovals.isEditable;

const currentApprovalValue = getApprovalValue(
tokenApprovals.approvals[0]!,
getTokenPrice,
);
const requestedApproval = requestedApprovalRef.current;

if (!currentApprovalValue || !requestedApproval) {
return null;
}

const requestedApprovalValue = getApprovalValue(
requestedApproval,
getTokenPrice,
);

if (!requestedApprovalValue) {
return null;
}

return (
<Stack width="100%" gap={1}>
<DetailsSection sx={{ py: 2 }}>
{tokenApprovals.approvals.map((approval) => (
<TokenSpendLimitCard
key={approval.token.address}
approval={approval}
approvalValue={currentApprovalValue}
/>
))}
</DetailsSection>
{hasOnlyOneEditableApproval && requestedApproval && (
<CustomApprovalLimit
action={action}
requestedValue={requestedApprovalValue}
approval={tokenApprovals.approvals[0]!}
approvalValue={currentApprovalValue}
isUnlimitedRequested={isUnlimitedApproval(requestedApproval)}
/>
)}
</Stack>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { styled } from '@avalabs/k2-alpine';

export const AmountInput = styled('input')(({ theme }) => ({
background: 'transparent',
border: 0,
paddingInline: theme.spacing(0),
lineHeight: 1,
outline: 'none',
color: theme.palette.text.primary,
textAlign: 'end',
textOverflow: 'ellipsis',
'&::-webkit-outer-spin-button, &::-webkit-inner-spin-button': {
'-webkit-appearance': 'none',
margin: 0,
},
'input[type=number]': {
'-moz-appearance': 'textfield',
},
}));
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { MaxUint256 } from 'ethers';
import { useTranslation } from 'react-i18next';
import { FC, useCallback, useState } from 'react';
import { DisplayData, TokenApproval } from '@avalabs/vm-module-types';

import { useConnectionContext } from '@core/ui';
import type { UpdateActionTxDataHandler } from '@core/service-worker';
import { Action, EnsureDefined, ExtensionRequest } from '@core/types';

import { DetailsSection } from '../../../generic/DetailsSection';
import { TxDetailsRow } from '../../../generic/DetailsItem/items/DetailRow';

import { ApprovalValue, SpendLimit } from '../types';
import { CustomLimitTrigger } from './CustomLimitTrigger';
import { CustomApprovalLimitOverlay } from './CustomApprovalLimitOverlay';

type CustomApprovalLimitProps = {
action: Action<EnsureDefined<DisplayData, 'tokenApprovals'>>;
approval: TokenApproval;
requestedValue: ApprovalValue;
approvalValue: ApprovalValue;
isUnlimitedRequested: boolean;
};

export const CustomApprovalLimit: FC<CustomApprovalLimitProps> = ({
action,
approval,
requestedValue,
approvalValue,
isUnlimitedRequested,
}) => {
const { t } = useTranslation();
const { request } = useConnectionContext();
const [isDialogOpen, setIsDialogOpen] = useState(false);

const { tokenValue } = approvalValue;

const [spendLimit, setSpendLimit] = useState<SpendLimit>({
type: isUnlimitedRequested ? 'unlimited' : 'requested',
value: tokenValue?.toSubUnit() ?? 0n,
});

const updateApprovalLimit = useCallback(() => {
if (!action.actionId) return;

const limitAmount =
spendLimit.type === 'unlimited' ? MaxUint256 : (spendLimit.value ?? 0n);

request<UpdateActionTxDataHandler>({
method: ExtensionRequest.ACTION_UPDATE_TX_DATA,
params: [
action.actionId,
{ approvalLimit: `0x${limitAmount.toString(16)}` },
],
});
}, [request, action.actionId, spendLimit]);

const handleSave = () => {
setIsDialogOpen(false);
updateApprovalLimit();
};

if (!tokenValue) {
return null;
}

return (
<>
<DetailsSection>
<TxDetailsRow label={t('Spend limit')}>
<CustomLimitTrigger
approval={approval}
approvalValue={approvalValue}
spendLimit={spendLimit}
onClick={() => setIsDialogOpen(true)}
/>
</TxDetailsRow>
<CustomApprovalLimitOverlay
isDialogOpen={isDialogOpen}
onClose={() => setIsDialogOpen(false)}
spendLimit={spendLimit}
setSpendLimit={setSpendLimit}
approval={approval}
requestedValue={requestedValue}
isUnlimitedRequested={isUnlimitedRequested}
onSave={handleSave}
/>
</DetailsSection>
</>
);
};
Loading
Loading