mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Token allowance screen updated (#16157)
This commit is contained in:
parent
0336a3c006
commit
17c1fef9be
3
app/_locales/en/messages.json
generated
3
app/_locales/en/messages.json
generated
@ -2931,6 +2931,9 @@
|
|||||||
"message": "By revoking permission, the following $1 will no longer be able to access your $2",
|
"message": "By revoking permission, the following $1 will no longer be able to access your $2",
|
||||||
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
|
"description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
|
||||||
},
|
},
|
||||||
|
"revokeSpendingCap": {
|
||||||
|
"message": "Revoke spending cap for your"
|
||||||
|
},
|
||||||
"revokeSpendingCapTooltipText": {
|
"revokeSpendingCapTooltipText": {
|
||||||
"message": "This contract will be unable to spend any more of your current or future tokens."
|
"message": "This contract will be unable to spend any more of your current or future tokens."
|
||||||
},
|
},
|
||||||
|
@ -217,25 +217,88 @@ export default function ApproveContentCard({
|
|||||||
}
|
}
|
||||||
|
|
||||||
ApproveContentCard.propTypes = {
|
ApproveContentCard.propTypes = {
|
||||||
|
/**
|
||||||
|
* Whether to show header including icon, transaction fee text and edit button
|
||||||
|
*/
|
||||||
showHeader: PropTypes.bool,
|
showHeader: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Symbol icon
|
||||||
|
*/
|
||||||
symbol: PropTypes.node,
|
symbol: PropTypes.node,
|
||||||
|
/**
|
||||||
|
* Title to be included in the header
|
||||||
|
*/
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
|
/**
|
||||||
|
* Whether to show edit button or not
|
||||||
|
*/
|
||||||
showEdit: PropTypes.bool,
|
showEdit: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Whether to show advanced gas fee options or not
|
||||||
|
*/
|
||||||
showAdvanceGasFeeOptions: PropTypes.bool,
|
showAdvanceGasFeeOptions: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Should open customize gas modal when edit button is clicked
|
||||||
|
*/
|
||||||
onEditClick: PropTypes.func,
|
onEditClick: PropTypes.func,
|
||||||
|
/**
|
||||||
|
* Footer to be shown
|
||||||
|
*/
|
||||||
footer: PropTypes.node,
|
footer: PropTypes.node,
|
||||||
|
/**
|
||||||
|
* Whether to include border-bottom or not
|
||||||
|
*/
|
||||||
noBorder: PropTypes.bool,
|
noBorder: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Is enhanced gas fee enabled or not
|
||||||
|
*/
|
||||||
supportsEIP1559V2: PropTypes.bool,
|
supportsEIP1559V2: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Whether to render transaction details content or not
|
||||||
|
*/
|
||||||
renderTransactionDetailsContent: PropTypes.bool,
|
renderTransactionDetailsContent: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Whether to render data content or not
|
||||||
|
*/
|
||||||
renderDataContent: PropTypes.bool,
|
renderDataContent: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Is multi-layer fee network or not
|
||||||
|
*/
|
||||||
isMultiLayerFeeNetwork: PropTypes.bool,
|
isMultiLayerFeeNetwork: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Total sum of the transaction in native currency
|
||||||
|
*/
|
||||||
ethTransactionTotal: PropTypes.string,
|
ethTransactionTotal: PropTypes.string,
|
||||||
|
/**
|
||||||
|
* Current native currency
|
||||||
|
*/
|
||||||
nativeCurrency: PropTypes.string,
|
nativeCurrency: PropTypes.string,
|
||||||
|
/**
|
||||||
|
* Current transaction
|
||||||
|
*/
|
||||||
fullTxData: PropTypes.object,
|
fullTxData: PropTypes.object,
|
||||||
|
/**
|
||||||
|
* Total sum of the transaction converted to hex value
|
||||||
|
*/
|
||||||
hexTransactionTotal: PropTypes.string,
|
hexTransactionTotal: PropTypes.string,
|
||||||
|
/**
|
||||||
|
* Total sum of the transaction in fiat currency
|
||||||
|
*/
|
||||||
fiatTransactionTotal: PropTypes.string,
|
fiatTransactionTotal: PropTypes.string,
|
||||||
|
/**
|
||||||
|
* Current fiat currency
|
||||||
|
*/
|
||||||
currentCurrency: PropTypes.string,
|
currentCurrency: PropTypes.string,
|
||||||
|
/**
|
||||||
|
* Is set approve for all or not
|
||||||
|
*/
|
||||||
isSetApproveForAll: PropTypes.bool,
|
isSetApproveForAll: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Whether a current set approval for all transaction will approve or revoke access
|
||||||
|
*/
|
||||||
isApprovalOrRejection: PropTypes.bool,
|
isApprovalOrRejection: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Current transaction data
|
||||||
|
*/
|
||||||
data: PropTypes.string,
|
data: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,196 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ApproveContentCard from './approve-content-card';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/App/ApproveContentCard',
|
||||||
|
id: __filename,
|
||||||
|
argTypes: {
|
||||||
|
showHeader: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
symbol: {
|
||||||
|
control: 'array',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
|
showEdit: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
showAdvanceGasFeeOptions: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
control: 'array',
|
||||||
|
},
|
||||||
|
noBorder: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
supportsEIP1559V2: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
renderTransactionDetailsContent: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
renderDataContent: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
isMultiLayerFeeNetwork: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
ethTransactionTotal: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
|
nativeCurrency: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
|
fullTxData: {
|
||||||
|
control: 'object',
|
||||||
|
},
|
||||||
|
hexTransactionTotal: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
|
fiatTransactionTotal: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
|
currentCurrency: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
|
isSetApproveForAll: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
isApprovalOrRejection: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
|
onEditClick: {
|
||||||
|
control: 'onEditClick',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
showHeader: true,
|
||||||
|
symbol: <i className="fa fa-tag" />,
|
||||||
|
title: 'Transaction fee',
|
||||||
|
showEdit: true,
|
||||||
|
showAdvanceGasFeeOptions: true,
|
||||||
|
noBorder: true,
|
||||||
|
supportsEIP1559V2: false,
|
||||||
|
renderTransactionDetailsContent: true,
|
||||||
|
renderDataContent: false,
|
||||||
|
isMultiLayerFeeNetwork: false,
|
||||||
|
ethTransactionTotal: '0.0012',
|
||||||
|
nativeCurrency: 'GoerliETH',
|
||||||
|
hexTransactionTotal: '0x44364c5bb0000',
|
||||||
|
fiatTransactionTotal: '1.54',
|
||||||
|
currentCurrency: 'usd',
|
||||||
|
isSetApproveForAll: false,
|
||||||
|
isApprovalOrRejection: false,
|
||||||
|
data: '',
|
||||||
|
fullTxData: {
|
||||||
|
id: 3049568294499567,
|
||||||
|
time: 1664449552289,
|
||||||
|
status: 'unapproved',
|
||||||
|
metamaskNetworkId: '3',
|
||||||
|
originalGasEstimate: '0xea60',
|
||||||
|
userEditedGasLimit: false,
|
||||||
|
chainId: '0x3',
|
||||||
|
loadingDefaults: false,
|
||||||
|
dappSuggestedGasFees: {
|
||||||
|
gasPrice: '0x4a817c800',
|
||||||
|
gas: '0xea60',
|
||||||
|
},
|
||||||
|
sendFlowHistory: [],
|
||||||
|
txParams: {
|
||||||
|
from: '0xdd34b35ca1de17dfcdc07f79ff1f8f94868c40a1',
|
||||||
|
to: '0x55797717b9947b31306f4aac7ad1365c6e3923bd',
|
||||||
|
value: '0x0',
|
||||||
|
data: '0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170',
|
||||||
|
gas: '0xea60',
|
||||||
|
maxFeePerGas: '0x4a817c800',
|
||||||
|
maxPriorityFeePerGas: '0x4a817c800',
|
||||||
|
},
|
||||||
|
origin: 'https://metamask.github.io',
|
||||||
|
type: 'approve',
|
||||||
|
history: [
|
||||||
|
{
|
||||||
|
id: 3049568294499567,
|
||||||
|
time: 1664449552289,
|
||||||
|
status: 'unapproved',
|
||||||
|
metamaskNetworkId: '3',
|
||||||
|
originalGasEstimate: '0xea60',
|
||||||
|
userEditedGasLimit: false,
|
||||||
|
chainId: '0x3',
|
||||||
|
loadingDefaults: true,
|
||||||
|
dappSuggestedGasFees: {
|
||||||
|
gasPrice: '0x4a817c800',
|
||||||
|
gas: '0xea60',
|
||||||
|
},
|
||||||
|
sendFlowHistory: [],
|
||||||
|
txParams: {
|
||||||
|
from: '0xdd34b35ca1de17dfcdc07f79ff1f8f94868c40a1',
|
||||||
|
to: '0x55797717b9947b31306f4aac7ad1365c6e3923bd',
|
||||||
|
value: '0x0',
|
||||||
|
data: '0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170',
|
||||||
|
gas: '0xea60',
|
||||||
|
gasPrice: '0x4a817c800',
|
||||||
|
},
|
||||||
|
origin: 'https://metamask.github.io',
|
||||||
|
type: 'approve',
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
op: 'remove',
|
||||||
|
path: '/txParams/gasPrice',
|
||||||
|
note: 'Added new unapproved transaction.',
|
||||||
|
timestamp: 1664449553939,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: 'add',
|
||||||
|
path: '/txParams/maxFeePerGas',
|
||||||
|
value: '0x4a817c800',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: 'add',
|
||||||
|
path: '/txParams/maxPriorityFeePerGas',
|
||||||
|
value: '0x4a817c800',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: 'replace',
|
||||||
|
path: '/loadingDefaults',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: 'add',
|
||||||
|
path: '/userFeeLevel',
|
||||||
|
value: 'custom',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: 'add',
|
||||||
|
path: '/defaultGasEstimates',
|
||||||
|
value: {
|
||||||
|
estimateType: 'custom',
|
||||||
|
gas: '0xea60',
|
||||||
|
maxFeePerGas: '0x4a817c800',
|
||||||
|
maxPriorityFeePerGas: '0x4a817c800',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
userFeeLevel: 'custom',
|
||||||
|
defaultGasEstimates: {
|
||||||
|
estimateType: 'custom',
|
||||||
|
gas: '0xea60',
|
||||||
|
maxFeePerGas: '0x4a817c800',
|
||||||
|
maxPriorityFeePerGas: '0x4a817c800',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => {
|
||||||
|
return <ApproveContentCard {...args} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useContext } from 'react';
|
import React, { useState, useContext, useEffect } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
import Box from '../../ui/box';
|
import Box from '../../ui/box';
|
||||||
@ -15,6 +16,8 @@ import {
|
|||||||
JUSTIFY_CONTENT,
|
JUSTIFY_CONTENT,
|
||||||
SIZES,
|
SIZES,
|
||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
|
import { getCustomTokenAmount } from '../../../selectors';
|
||||||
|
import { setCustomTokenAmount } from '../../../ducks/app/app';
|
||||||
import { CustomSpendingCapTooltip } from './custom-spending-cap-tooltip';
|
import { CustomSpendingCapTooltip } from './custom-spending-cap-tooltip';
|
||||||
|
|
||||||
export default function CustomSpendingCap({
|
export default function CustomSpendingCap({
|
||||||
@ -22,12 +25,17 @@ export default function CustomSpendingCap({
|
|||||||
currentTokenBalance,
|
currentTokenBalance,
|
||||||
dappProposedValue,
|
dappProposedValue,
|
||||||
siteOrigin,
|
siteOrigin,
|
||||||
onEdit,
|
passTheErrorText,
|
||||||
}) {
|
}) {
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
const [value, setValue] = useState('');
|
const dispatch = useDispatch();
|
||||||
const [customSpendingCapText, setCustomSpendingCapText] = useState('');
|
|
||||||
|
const value = useSelector(getCustomTokenAmount);
|
||||||
|
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
const [showUseDefaultButton, setShowUseDefaultButton] = useState(
|
||||||
|
value !== String(dappProposedValue) && true,
|
||||||
|
);
|
||||||
const inputLogicEmptyStateText = t('inputLogicEmptyState');
|
const inputLogicEmptyStateText = t('inputLogicEmptyState');
|
||||||
|
|
||||||
const getInputTextLogic = (inputNumber) => {
|
const getInputTextLogic = (inputNumber) => {
|
||||||
@ -57,6 +65,10 @@ export default function CustomSpendingCap({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [customSpendingCapText, setCustomSpendingCapText] = useState(
|
||||||
|
getInputTextLogic(value).description,
|
||||||
|
);
|
||||||
|
|
||||||
const handleChange = (valueInput) => {
|
const handleChange = (valueInput) => {
|
||||||
let spendingCapError = '';
|
let spendingCapError = '';
|
||||||
const inputTextLogic = getInputTextLogic(valueInput);
|
const inputTextLogic = getInputTextLogic(valueInput);
|
||||||
@ -71,9 +83,19 @@ export default function CustomSpendingCap({
|
|||||||
setError('');
|
setError('');
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue(valueInput);
|
dispatch(setCustomTokenAmount(String(valueInput)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (value !== String(dappProposedValue)) {
|
||||||
|
setShowUseDefaultButton(true);
|
||||||
|
}
|
||||||
|
}, [value, dappProposedValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
passTheErrorText(error);
|
||||||
|
}, [error, passTheErrorText]);
|
||||||
|
|
||||||
const chooseTooltipContentText =
|
const chooseTooltipContentText =
|
||||||
value > currentTokenBalance
|
value > currentTokenBalance
|
||||||
? t('warningTooltipText', [
|
? t('warningTooltipText', [
|
||||||
@ -100,7 +122,6 @@ export default function CustomSpendingCap({
|
|||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleChange(currentTokenBalance);
|
handleChange(currentTokenBalance);
|
||||||
setValue(currentTokenBalance);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('max')}
|
{t('max')}
|
||||||
@ -131,6 +152,7 @@ export default function CustomSpendingCap({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
|
numeric
|
||||||
dataTestId="custom-spending-cap-input"
|
dataTestId="custom-spending-cap-input"
|
||||||
autoFocus
|
autoFocus
|
||||||
wrappingLabelProps={{ as: 'div' }}
|
wrappingLabelProps={{ as: 'div' }}
|
||||||
@ -151,21 +173,19 @@ export default function CustomSpendingCap({
|
|||||||
error={error}
|
error={error}
|
||||||
value={value}
|
value={value}
|
||||||
titleDetail={
|
titleDetail={
|
||||||
<button
|
showUseDefaultButton && (
|
||||||
className="custom-spending-cap__input--button"
|
<button
|
||||||
type="link"
|
className="custom-spending-cap__input--button"
|
||||||
onClick={(e) => {
|
type="link"
|
||||||
e.preventDefault();
|
onClick={(e) => {
|
||||||
if (value <= currentTokenBalance || error) {
|
e.preventDefault();
|
||||||
|
setShowUseDefaultButton(false);
|
||||||
handleChange(dappProposedValue);
|
handleChange(dappProposedValue);
|
||||||
setValue(dappProposedValue);
|
}}
|
||||||
} else {
|
>
|
||||||
onEdit();
|
{t('useDefault')}
|
||||||
}
|
</button>
|
||||||
}}
|
)
|
||||||
>
|
|
||||||
{value > currentTokenBalance ? t('edit') : t('useDefault')}
|
|
||||||
</button>
|
|
||||||
}
|
}
|
||||||
titleDetailWrapperProps={{ marginBottom: 2, marginRight: 0 }}
|
titleDetailWrapperProps={{ marginBottom: 2, marginRight: 0 }}
|
||||||
allowDecimals
|
allowDecimals
|
||||||
@ -202,7 +222,7 @@ CustomSpendingCap.propTypes = {
|
|||||||
*/
|
*/
|
||||||
siteOrigin: PropTypes.string,
|
siteOrigin: PropTypes.string,
|
||||||
/**
|
/**
|
||||||
* onClick handler for the Edit link
|
* Parent component's callback function passed in order to get the error text
|
||||||
*/
|
*/
|
||||||
onEdit: PropTypes.func,
|
passTheErrorText: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
@ -17,8 +17,8 @@ export default {
|
|||||||
siteOrigin: {
|
siteOrigin: {
|
||||||
control: { type: 'text' },
|
control: { type: 'text' },
|
||||||
},
|
},
|
||||||
onEdit: {
|
passTheErrorText: {
|
||||||
action: 'onEdit',
|
action: 'passTheErrorText',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
margin-top: 55px;
|
margin-top: 55px;
|
||||||
margin-inline-start: -75px;
|
margin-inline-start: -75px;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,4 +29,10 @@
|
|||||||
color: var(--color-error-default);
|
color: var(--color-error-default);
|
||||||
padding-inline-end: 60px;
|
padding-inline-end: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type='number']::-webkit-inner-spin-button,
|
||||||
|
input[type='number']:hover::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ export default function reduceApp(state = {}, action) {
|
|||||||
newTokensImported: '',
|
newTokensImported: '',
|
||||||
newCustomNetworkAdded: {},
|
newCustomNetworkAdded: {},
|
||||||
onboardedInThisUISession: false,
|
onboardedInThisUISession: false,
|
||||||
|
customTokenAmount: '',
|
||||||
...state,
|
...state,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -401,6 +402,11 @@ export default function reduceApp(state = {}, action) {
|
|||||||
...appState,
|
...appState,
|
||||||
onboardedInThisUISession: action.value,
|
onboardedInThisUISession: action.value,
|
||||||
};
|
};
|
||||||
|
case actionConstants.SET_CUSTOM_TOKEN_AMOUNT:
|
||||||
|
return {
|
||||||
|
...appState,
|
||||||
|
customTokenAmount: action.value,
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return appState;
|
return appState;
|
||||||
}
|
}
|
||||||
@ -463,3 +469,7 @@ export function setNewCustomNetworkAdded(value) {
|
|||||||
export function setOnBoardedInThisUISession(value) {
|
export function setOnBoardedInThisUISession(value) {
|
||||||
return { type: actionConstants.ONBOARDED_IN_THIS_UI_SESSION, value };
|
return { type: actionConstants.ONBOARDED_IN_THIS_UI_SESSION, value };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setCustomTokenAmount(value) {
|
||||||
|
return { type: actionConstants.SET_CUSTOM_TOKEN_AMOUNT, value };
|
||||||
|
}
|
||||||
|
@ -180,14 +180,14 @@ export default function ConfirmApprove({
|
|||||||
supportsEIP1559V2={supportsEIP1559V2}
|
supportsEIP1559V2={supportsEIP1559V2}
|
||||||
userAddress={userAddress}
|
userAddress={userAddress}
|
||||||
tokenAddress={tokenAddress}
|
tokenAddress={tokenAddress}
|
||||||
data={customData || transactionData}
|
data={transactionData}
|
||||||
isSetApproveForAll={isSetApproveForAll}
|
isSetApproveForAll={isSetApproveForAll}
|
||||||
isApprovalOrRejection={isApprovalOrRejection}
|
isApprovalOrRejection={isApprovalOrRejection}
|
||||||
customTxParamsData={customData}
|
|
||||||
dappProposedTokenAmount={tokenAmount}
|
dappProposedTokenAmount={tokenAmount}
|
||||||
currentTokenBalance={tokenBalance}
|
currentTokenBalance={tokenBalance}
|
||||||
toAddress={toAddress}
|
toAddress={toAddress}
|
||||||
tokenSymbol={tokenSymbol}
|
tokenSymbol={tokenSymbol}
|
||||||
|
decimals={decimals}
|
||||||
/>
|
/>
|
||||||
{showCustomizeGasPopover && !supportsEIP1559V2 && (
|
{showCustomizeGasPopover && !supportsEIP1559V2 && (
|
||||||
<EditGasPopover
|
<EditGasPopover
|
||||||
|
@ -29,6 +29,7 @@ import {
|
|||||||
transactionFeeSelector,
|
transactionFeeSelector,
|
||||||
getKnownMethodData,
|
getKnownMethodData,
|
||||||
getRpcPrefsForCurrentProvider,
|
getRpcPrefsForCurrentProvider,
|
||||||
|
getCustomTokenAmount,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
import { NETWORK_TO_NAME_MAP } from '../../../shared/constants/network';
|
import { NETWORK_TO_NAME_MAP } from '../../../shared/constants/network';
|
||||||
import {
|
import {
|
||||||
@ -39,6 +40,10 @@ import {
|
|||||||
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck';
|
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck';
|
||||||
import { getMostRecentOverviewPage } from '../../ducks/history/history';
|
import { getMostRecentOverviewPage } from '../../ducks/history/history';
|
||||||
import ApproveContentCard from '../../components/app/approve-content-card/approve-content-card';
|
import ApproveContentCard from '../../components/app/approve-content-card/approve-content-card';
|
||||||
|
import CustomSpendingCap from '../../components/app/custom-spending-cap/custom-spending-cap';
|
||||||
|
import Dialog from '../../components/ui/dialog';
|
||||||
|
import { useGasFeeContext } from '../../contexts/gasFee';
|
||||||
|
import { getCustomTxParamsData } from '../confirm-approve/confirm-approve.util';
|
||||||
|
|
||||||
export default function TokenAllowance({
|
export default function TokenAllowance({
|
||||||
origin,
|
origin,
|
||||||
@ -58,7 +63,7 @@ export default function TokenAllowance({
|
|||||||
data,
|
data,
|
||||||
isSetApproveForAll,
|
isSetApproveForAll,
|
||||||
isApprovalOrRejection,
|
isApprovalOrRejection,
|
||||||
customTxParamsData,
|
decimals,
|
||||||
dappProposedTokenAmount,
|
dappProposedTokenAmount,
|
||||||
currentTokenBalance,
|
currentTokenBalance,
|
||||||
toAddress,
|
toAddress,
|
||||||
@ -71,11 +76,22 @@ export default function TokenAllowance({
|
|||||||
|
|
||||||
const [showContractDetails, setShowContractDetails] = useState(false);
|
const [showContractDetails, setShowContractDetails] = useState(false);
|
||||||
const [showFullTxDetails, setShowFullTxDetails] = useState(false);
|
const [showFullTxDetails, setShowFullTxDetails] = useState(false);
|
||||||
const [isFirstPage, setIsFirstPage] = useState(false);
|
const [isFirstPage, setIsFirstPage] = useState(true);
|
||||||
|
const [errorText, setErrorText] = useState('');
|
||||||
|
|
||||||
const currentAccount = useSelector(getCurrentAccountWithSendEtherInfo);
|
const currentAccount = useSelector(getCurrentAccountWithSendEtherInfo);
|
||||||
const networkIdentifier = useSelector(getNetworkIdentifier);
|
const networkIdentifier = useSelector(getNetworkIdentifier);
|
||||||
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
|
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
|
||||||
|
const customTokenAmount = useSelector(getCustomTokenAmount);
|
||||||
|
|
||||||
|
const customPermissionAmount = customTokenAmount.toString();
|
||||||
|
|
||||||
|
const customTxParamsData = customTokenAmount
|
||||||
|
? getCustomTxParamsData(data, {
|
||||||
|
customPermissionAmount,
|
||||||
|
decimals,
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
let fullTxData = { ...txData };
|
let fullTxData = { ...txData };
|
||||||
|
|
||||||
@ -92,6 +108,13 @@ export default function TokenAllowance({
|
|||||||
const fee = useSelector((state) => transactionFeeSelector(state, fullTxData));
|
const fee = useSelector((state) => transactionFeeSelector(state, fullTxData));
|
||||||
const methodData = useSelector((state) => getKnownMethodData(state, data));
|
const methodData = useSelector((state) => getKnownMethodData(state, data));
|
||||||
|
|
||||||
|
const { balanceError } = useGasFeeContext();
|
||||||
|
|
||||||
|
const disableNextButton =
|
||||||
|
isFirstPage && (customTokenAmount === '' || errorText !== '');
|
||||||
|
|
||||||
|
const disableApproveButton = !isFirstPage && balanceError;
|
||||||
|
|
||||||
const networkName =
|
const networkName =
|
||||||
NETWORK_TO_NAME_MAP[fullTxData.chainId] || networkIdentifier;
|
NETWORK_TO_NAME_MAP[fullTxData.chainId] || networkIdentifier;
|
||||||
|
|
||||||
@ -105,9 +128,10 @@ export default function TokenAllowance({
|
|||||||
: transactionData;
|
: transactionData;
|
||||||
|
|
||||||
const handleReject = () => {
|
const handleReject = () => {
|
||||||
|
dispatch(updateCustomNonce(''));
|
||||||
|
|
||||||
dispatch(cancelTx(fullTxData)).then(() => {
|
dispatch(cancelTx(fullTxData)).then(() => {
|
||||||
dispatch(clearConfirmTransaction());
|
dispatch(clearConfirmTransaction());
|
||||||
dispatch(updateCustomNonce(''));
|
|
||||||
history.push(mostRecentOverviewPage);
|
history.push(mostRecentOverviewPage);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -128,17 +152,35 @@ export default function TokenAllowance({
|
|||||||
fullTxData.originalApprovalAmount = dappProposedTokenAmount;
|
fullTxData.originalApprovalAmount = dappProposedTokenAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (customTokenAmount) {
|
||||||
|
fullTxData.customTokenAmount = customTokenAmount;
|
||||||
|
fullTxData.finalApprovalAmount = customTokenAmount;
|
||||||
|
} else if (dappProposedTokenAmount !== undefined) {
|
||||||
|
fullTxData.finalApprovalAmount = dappProposedTokenAmount;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentTokenBalance) {
|
if (currentTokenBalance) {
|
||||||
fullTxData.currentTokenBalance = currentTokenBalance;
|
fullTxData.currentTokenBalance = currentTokenBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispatch(updateCustomNonce(''));
|
||||||
|
|
||||||
dispatch(updateAndApproveTx(customNonceMerge(fullTxData))).then(() => {
|
dispatch(updateAndApproveTx(customNonceMerge(fullTxData))).then(() => {
|
||||||
dispatch(clearConfirmTransaction());
|
dispatch(clearConfirmTransaction());
|
||||||
dispatch(updateCustomNonce(''));
|
|
||||||
history.push(mostRecentOverviewPage);
|
history.push(mostRecentOverviewPage);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleNextClick = () => {
|
||||||
|
setShowFullTxDetails(false);
|
||||||
|
setIsFirstPage(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBackClick = () => {
|
||||||
|
setShowFullTxDetails(false);
|
||||||
|
setIsFirstPage(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="token-allowance-container page-container">
|
<Box className="token-allowance-container page-container">
|
||||||
<Box
|
<Box
|
||||||
@ -151,7 +193,7 @@ export default function TokenAllowance({
|
|||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
{!isFirstPage && (
|
{!isFirstPage && (
|
||||||
<Button type="inline" onClick={() => setIsFirstPage(true)}>
|
<Button type="inline" onClick={() => handleBackClick()}>
|
||||||
<Typography
|
<Typography
|
||||||
variant={TYPOGRAPHY.H6}
|
variant={TYPOGRAPHY.H6}
|
||||||
color={COLORS.TEXT_MUTED}
|
color={COLORS.TEXT_MUTED}
|
||||||
@ -217,13 +259,17 @@ export default function TokenAllowance({
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box marginBottom={5}>
|
<Box marginBottom={5} marginLeft={4} marginRight={4}>
|
||||||
<Typography
|
<Typography
|
||||||
variant={TYPOGRAPHY.H3}
|
variant={TYPOGRAPHY.H3}
|
||||||
fontWeight={FONT_WEIGHT.BOLD}
|
fontWeight={FONT_WEIGHT.BOLD}
|
||||||
align={TEXT_ALIGN.CENTER}
|
align={TEXT_ALIGN.CENTER}
|
||||||
>
|
>
|
||||||
{isFirstPage ? t('setSpendingCap') : t('reviewSpendingCap')}
|
{isFirstPage && t('setSpendingCap')}
|
||||||
|
{!isFirstPage &&
|
||||||
|
(customTokenAmount === 0
|
||||||
|
? t('revokeSpendingCap')
|
||||||
|
: t('reviewSpendingCap'))}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
@ -251,13 +297,28 @@ export default function TokenAllowance({
|
|||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Box margin={[4, 4, 3, 4]}>
|
<Box margin={[4, 4, 3, 4]}>
|
||||||
<ReviewSpendingCap
|
{isFirstPage ? (
|
||||||
tokenName={tokenSymbol}
|
<CustomSpendingCap
|
||||||
currentTokenBalance={parseFloat(currentTokenBalance)}
|
tokenName={tokenSymbol}
|
||||||
tokenValue={10}
|
currentTokenBalance={parseFloat(currentTokenBalance)}
|
||||||
onEdit={() => setIsFirstPage(true)}
|
dappProposedValue={parseFloat(dappProposedTokenAmount)}
|
||||||
/>
|
siteOrigin={origin}
|
||||||
|
passTheErrorText={(value) => setErrorText(value)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ReviewSpendingCap
|
||||||
|
tokenName={tokenSymbol}
|
||||||
|
currentTokenBalance={parseFloat(currentTokenBalance)}
|
||||||
|
tokenValue={parseFloat(customTokenAmount)}
|
||||||
|
onEdit={() => handleBackClick()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
{!isFirstPage && balanceError && (
|
||||||
|
<Dialog type="error" className="send__error-dialog">
|
||||||
|
{t('insufficientFundsForGas')}
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
{!isFirstPage && (
|
{!isFirstPage && (
|
||||||
<Box className="token-allowance-container__card-wrapper">
|
<Box className="token-allowance-container__card-wrapper">
|
||||||
<ApproveContentCard
|
<ApproveContentCard
|
||||||
@ -319,7 +380,7 @@ export default function TokenAllowance({
|
|||||||
supportsEIP1559V2={supportsEIP1559V2}
|
supportsEIP1559V2={supportsEIP1559V2}
|
||||||
isSetApproveForAll={isSetApproveForAll}
|
isSetApproveForAll={isSetApproveForAll}
|
||||||
isApprovalOrRejection={isApprovalOrRejection}
|
isApprovalOrRejection={isApprovalOrRejection}
|
||||||
data={data}
|
data={customTxParamsData || data}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@ -328,7 +389,8 @@ export default function TokenAllowance({
|
|||||||
cancelText={t('reject')}
|
cancelText={t('reject')}
|
||||||
submitText={isFirstPage ? t('next') : t('approveButtonText')}
|
submitText={isFirstPage ? t('next') : t('approveButtonText')}
|
||||||
onCancel={() => handleReject()}
|
onCancel={() => handleReject()}
|
||||||
onSubmit={() => (isFirstPage ? setIsFirstPage(false) : handleApprove())}
|
onSubmit={() => (isFirstPage ? handleNextClick() : handleApprove())}
|
||||||
|
disabled={disableNextButton || disableApproveButton}
|
||||||
/>
|
/>
|
||||||
{showContractDetails && (
|
{showContractDetails && (
|
||||||
<ContractDetailsModal
|
<ContractDetailsModal
|
||||||
@ -416,9 +478,9 @@ TokenAllowance.propTypes = {
|
|||||||
*/
|
*/
|
||||||
isApprovalOrRejection: PropTypes.bool,
|
isApprovalOrRejection: PropTypes.bool,
|
||||||
/**
|
/**
|
||||||
* Custom transaction parameters data made by the user (fees)
|
* Number of decimals
|
||||||
*/
|
*/
|
||||||
customTxParamsData: PropTypes.object,
|
decimals: PropTypes.string,
|
||||||
/**
|
/**
|
||||||
* Token amount proposed by the Dapp
|
* Token amount proposed by the Dapp
|
||||||
*/
|
*/
|
||||||
|
@ -53,8 +53,8 @@ export default {
|
|||||||
setApproveForAllArg: {
|
setApproveForAllArg: {
|
||||||
control: 'boolean',
|
control: 'boolean',
|
||||||
},
|
},
|
||||||
customTxParamsData: {
|
decimals: {
|
||||||
control: 'object',
|
control: 'text',
|
||||||
},
|
},
|
||||||
dappProposedTokenAmount: {
|
dappProposedTokenAmount: {
|
||||||
control: 'text',
|
control: 'text',
|
||||||
@ -88,7 +88,7 @@ export default {
|
|||||||
data: '0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170',
|
data: '0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170',
|
||||||
isSetApproveForAll: false,
|
isSetApproveForAll: false,
|
||||||
setApproveForAllArg: false,
|
setApproveForAllArg: false,
|
||||||
customTxParamsData: {},
|
decimals: '4',
|
||||||
dappProposedTokenAmount: '7',
|
dappProposedTokenAmount: '7',
|
||||||
currentTokenBalance: '10',
|
currentTokenBalance: '10',
|
||||||
toAddress: '0x9bc5baf874d2da8d216ae9f137804184ee5afef4',
|
toAddress: '0x9bc5baf874d2da8d216ae9f137804184ee5afef4',
|
||||||
|
@ -1307,3 +1307,7 @@ export function getShouldShowSeedPhraseReminder(state) {
|
|||||||
dismissSeedBackUpReminder === false
|
dismissSeedBackUpReminder === false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCustomTokenAmount(state) {
|
||||||
|
return state.appState.customTokenAmount;
|
||||||
|
}
|
||||||
|
@ -120,3 +120,6 @@ export const TOGGLE_CURRENCY_INPUT_SWITCH = 'TOGGLE_CURRENCY_INPUT_SWITCH';
|
|||||||
|
|
||||||
// Token detection v2
|
// Token detection v2
|
||||||
export const SET_NEW_TOKENS_IMPORTED = 'SET_NEW_TOKENS_IMPORTED';
|
export const SET_NEW_TOKENS_IMPORTED = 'SET_NEW_TOKENS_IMPORTED';
|
||||||
|
|
||||||
|
// Token allowance
|
||||||
|
export const SET_CUSTOM_TOKEN_AMOUNT = 'SET_CUSTOM_TOKEN_AMOUNT';
|
||||||
|
Loading…
Reference in New Issue
Block a user