mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
Add types to send state (#14740)
This commit is contained in:
parent
5b8a69c721
commit
51986a4724
@ -10,9 +10,22 @@ export const GAS_LIMITS = {
|
|||||||
BASE_TOKEN_ESTIMATE: addHexPrefix(ONE_HUNDRED_THOUSAND.toString(16)),
|
BASE_TOKEN_ESTIMATE: addHexPrefix(ONE_HUNDRED_THOUSAND.toString(16)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} GasEstimateTypes
|
||||||
|
* @property {'fee-market'} FEE_MARKET - A gas estimate for a fee market
|
||||||
|
* transaction generated by our gas estimation API.
|
||||||
|
* @property {'legacy'} LEGACY - A gas estimate for a legacy Transaction
|
||||||
|
* generated by our gas estimation API.
|
||||||
|
* @property {'eth_gasPrice'} ETH_GAS_PRICE - A gas estimate provided by the
|
||||||
|
* Ethereum node via eth_gasPrice.
|
||||||
|
* @property {'none'} NONE - No gas estimate available.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These are already declared in @metamask/controllers but importing them from
|
* These are already declared in @metamask/controllers but importing them from
|
||||||
* that module and re-exporting causes the UI bundle size to expand beyond 4MB
|
* that module and re-exporting causes the UI bundle size to expand beyond 4MB
|
||||||
|
*
|
||||||
|
* @type {GasEstimateTypes}
|
||||||
*/
|
*/
|
||||||
export const GAS_ESTIMATE_TYPES = {
|
export const GAS_ESTIMATE_TYPES = {
|
||||||
FEE_MARKET: 'fee-market',
|
FEE_MARKET: 'fee-market',
|
||||||
|
@ -323,13 +323,28 @@ export const TRANSACTION_EVENTS = {
|
|||||||
SUBMITTED: 'Transaction Submitted',
|
SUBMITTED: 'Transaction Submitted',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} AssetTypes
|
||||||
|
* @property {'NATIVE'} NATIVE - The native asset for the current network, such
|
||||||
|
* as ETH
|
||||||
|
* @property {'TOKEN'} TOKEN - An ERC20 token.
|
||||||
|
* @property {'COLLECTIBLE'} COLLECTIBLE - An ERC721 or ERC1155 token.
|
||||||
|
* @property {'UNKNOWN'} UNKNOWN - A transaction interacting with a contract
|
||||||
|
* that isn't a token method interaction will be marked as dealing with an
|
||||||
|
* unknown asset type.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type will work anywhere you expect a string that can be one of the
|
||||||
|
* above asset types
|
||||||
|
*
|
||||||
|
* @typedef {AssetTypes[keyof AssetTypes]} AssetTypesString
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The types of assets that a user can send
|
* The types of assets that a user can send
|
||||||
* 1. NATIVE - The native asset for the current network, such as ETH
|
*
|
||||||
* 2. TOKEN - An ERC20 token.
|
* @type {AssetTypes}
|
||||||
* 3. COLLECTIBLE - An ERC721 or ERC1155 token.
|
|
||||||
* 4. UNKNOWN - A transaction interacting with a contract that isn't a token
|
|
||||||
* method interaction will be marked as dealing with an unknown asset type.
|
|
||||||
*/
|
*/
|
||||||
export const ASSET_TYPES = {
|
export const ASSET_TYPES = {
|
||||||
NATIVE: 'NATIVE',
|
NATIVE: 'NATIVE',
|
||||||
|
@ -96,13 +96,7 @@ import { sumHexes } from '../../helpers/utils/transactions.util';
|
|||||||
import fetchEstimatedL1Fee from '../../helpers/utils/optimism/fetchEstimatedL1Fee';
|
import fetchEstimatedL1Fee from '../../helpers/utils/optimism/fetchEstimatedL1Fee';
|
||||||
|
|
||||||
import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../shared/constants/network';
|
import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../shared/constants/network';
|
||||||
import {
|
import { TOKEN_STANDARDS, ETH, GWEI } from '../../helpers/constants/common';
|
||||||
ERC20,
|
|
||||||
ERC721,
|
|
||||||
ERC1155,
|
|
||||||
ETH,
|
|
||||||
GWEI,
|
|
||||||
} from '../../helpers/constants/common';
|
|
||||||
import {
|
import {
|
||||||
ASSET_TYPES,
|
ASSET_TYPES,
|
||||||
TRANSACTION_ENVELOPE_TYPES,
|
TRANSACTION_ENVELOPE_TYPES,
|
||||||
@ -112,24 +106,61 @@ import { readAddressAsContract } from '../../../shared/modules/contract-utils';
|
|||||||
import { INVALID_ASSET_TYPE } from '../../helpers/constants/error-keys';
|
import { INVALID_ASSET_TYPE } from '../../helpers/constants/error-keys';
|
||||||
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
|
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
|
||||||
import { getValueFromWeiHex } from '../../helpers/utils/confirm-tx.util';
|
import { getValueFromWeiHex } from '../../helpers/utils/confirm-tx.util';
|
||||||
// typedefs
|
// typedef import statements
|
||||||
/**
|
/**
|
||||||
* @typedef {import('@reduxjs/toolkit').PayloadAction} PayloadAction
|
* @typedef {(
|
||||||
|
* import('immer/dist/internal').WritableDraft<SendState>
|
||||||
|
* )} SendStateDraft
|
||||||
|
* @typedef {(
|
||||||
|
* import('../../../shared/constants/transaction').AssetTypesString
|
||||||
|
* )} AssetTypesString
|
||||||
|
* @typedef {(
|
||||||
|
* import( '../../helpers/constants/common').TokenStandardStrings
|
||||||
|
* )} TokenStandardStrings
|
||||||
|
* @typedef {(
|
||||||
|
* import('../../../shared/constants/transaction').TransactionTypeString
|
||||||
|
* )} TransactionTypeString
|
||||||
|
* @typedef {(
|
||||||
|
* import('@metamask/controllers').LegacyGasPriceEstimate
|
||||||
|
* )} LegacyGasPriceEstimate
|
||||||
|
* @typedef {(
|
||||||
|
* import('@metamask/controllers').GasFeeEstimates
|
||||||
|
* )} GasFeeEstimates
|
||||||
|
* @typedef {(
|
||||||
|
* import('@metamask/controllers').EthGasPriceEstimate
|
||||||
|
* )} EthGasPriceEstimate
|
||||||
|
* @typedef {(
|
||||||
|
* import('@metamask/controllers').GasEstimateType
|
||||||
|
* )} GasEstimateType
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const name = 'send';
|
const name = 'send';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SendStateStages
|
||||||
|
* @property {'INACTIVE'} INACTIVE - The send state is idle, and hasn't yet
|
||||||
|
* fetched required data for gasPrice and gasLimit estimations, etc.
|
||||||
|
* @property {'ADD_RECIPIENT'} ADD_RECIPIENT - The user is selecting which
|
||||||
|
* address to send an asset to.
|
||||||
|
* @property {'DRAFT'} DRAFT - The send form is shown for a transaction yet to
|
||||||
|
* be sent to the Transaction Controller.
|
||||||
|
* @property {'EDIT'} EDIT - The send form is shown for a transaction already
|
||||||
|
* submitted to the Transaction Controller but not yet confirmed. This happens
|
||||||
|
* when a confirmation is shown for a transaction and the 'edit' button in the
|
||||||
|
* header is clicked.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type will work anywhere you expect a string that can be one of the
|
||||||
|
* above Stages
|
||||||
|
*
|
||||||
|
* @typedef {SendStateStages[keyof SendStateStages]} SendStateStagesStrings
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Stages that the send slice can be in
|
* The Stages that the send slice can be in
|
||||||
* 1. INACTIVE - The send state is idle, and hasn't yet fetched required
|
*
|
||||||
* data for gasPrice and gasLimit estimations, etc.
|
* @type {SendStateStages}
|
||||||
* 2. ADD_RECIPIENT - The user is selecting which address to send an asset to
|
|
||||||
* 3. DRAFT - The send form is shown for a transaction yet to be sent to the
|
|
||||||
* Transaction Controller.
|
|
||||||
* 4. EDIT - The send form is shown for a transaction already submitted to the
|
|
||||||
* Transaction Controller but not yet confirmed. This happens when a
|
|
||||||
* confirmation is shown for a transaction and the 'edit' button in the header
|
|
||||||
* is clicked.
|
|
||||||
*/
|
*/
|
||||||
export const SEND_STAGES = {
|
export const SEND_STAGES = {
|
||||||
INACTIVE: 'INACTIVE',
|
INACTIVE: 'INACTIVE',
|
||||||
@ -139,33 +170,59 @@ export const SEND_STAGES = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The status that the send slice can be in is either
|
* @typedef {Object} SendStateStatuses
|
||||||
* 1. VALID - the transaction is valid and can be submitted
|
* @property {'VALID'} VALID - The transaction is valid and can be submitted.
|
||||||
* 2. INVALID - the transaction is invalid and cannot be submitted
|
* @property {'INVALID'} INVALID - The transaction is invalid and cannot be
|
||||||
|
* submitted. There are a number of cases that would result in an invalid
|
||||||
|
* send state:
|
||||||
|
* 1. The recipient is not yet defined
|
||||||
|
* 2. The amount + gasTotal is greater than the user's balance when sending
|
||||||
|
* native currency
|
||||||
|
* 3. The gasTotal is greater than the user's *native* balance
|
||||||
|
* 4. The amount of sent asset is greater than the user's *asset* balance
|
||||||
|
* 5. Gas price estimates failed to load entirely
|
||||||
|
* 6. The gasLimit is less than 21000 (0x5208)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type will work anywhere you expect a string that can be one of the
|
||||||
|
* above statuses
|
||||||
*
|
*
|
||||||
* A number of cases would result in an invalid form
|
* @typedef {SendStateStatuses[keyof SendStateStatuses]} SendStateStatusStrings
|
||||||
* 1. The recipient is not yet defined
|
*/
|
||||||
* 2. The amount + gasTotal is greater than the user's balance when sending
|
|
||||||
* native currency
|
/**
|
||||||
* 3. The gasTotal is greater than the user's *native* balance
|
* The status of the send slice
|
||||||
* 4. The amount of sent asset is greater than the user's *asset* balance
|
*
|
||||||
* 5. Gas price estimates failed to load entirely
|
* @type {SendStateStatuses}
|
||||||
* 6. The gasLimit is less than 21000 (0x5208)
|
|
||||||
*/
|
*/
|
||||||
export const SEND_STATUSES = {
|
export const SEND_STATUSES = {
|
||||||
VALID: 'VALID',
|
VALID: 'VALID',
|
||||||
INVALID: 'INVALID',
|
INVALID: 'INVALID',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SendStateGasModes
|
||||||
|
* @property {'BASIC'} BASIC - Shows the basic estimate slow/avg/fast buttons
|
||||||
|
* when on mainnet and the metaswaps API request is successful.
|
||||||
|
* @property {'INLINE'} INLINE - Shows inline gasLimit/gasPrice fields when on
|
||||||
|
* any other network or metaswaps API fails and we use eth_gasPrice.
|
||||||
|
* @property {'CUSTOM'} CUSTOM - Shows GasFeeDisplay component that is a read
|
||||||
|
* only display of the values the user has set in the advanced gas modal
|
||||||
|
* (stored in the gas duck under the customData key).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type will work anywhere you expect a string that can be one of the
|
||||||
|
* above gas modes
|
||||||
|
*
|
||||||
|
* @typedef {SendStateGasModes[keyof SendStateGasModes]} SendStateGasModeStrings
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controls what is displayed in the send-gas-row component.
|
* Controls what is displayed in the send-gas-row component.
|
||||||
* 1. BASIC - Shows the basic estimate slow/avg/fast buttons when on mainnet
|
*
|
||||||
* and the metaswaps API request is successful.
|
* @type {SendStateGasModes}
|
||||||
* 2. INLINE - Shows inline gasLimit/gasPrice fields when on any other network
|
|
||||||
* or metaswaps API fails and we use eth_gasPrice
|
|
||||||
* 3. CUSTOM - Shows GasFeeDisplay component that is a read only display of the
|
|
||||||
* values the user has set in the advanced gas modal (stored in the gas duck
|
|
||||||
* under the customData key).
|
|
||||||
*/
|
*/
|
||||||
export const GAS_INPUT_MODES = {
|
export const GAS_INPUT_MODES = {
|
||||||
BASIC: 'BASIC',
|
BASIC: 'BASIC',
|
||||||
@ -173,17 +230,51 @@ export const GAS_INPUT_MODES = {
|
|||||||
CUSTOM: 'CUSTOM',
|
CUSTOM: 'CUSTOM',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SendStateAmountModes
|
||||||
|
* @property {'INPUT'} INPUT - the user provides the amount by typing in the
|
||||||
|
* field.
|
||||||
|
* @property {'MAX'} MAX - The user selects the MAX button and amount is
|
||||||
|
* calculated based on balance - (amount + gasTotal).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type will work anywhere you expect a string that can be one of the
|
||||||
|
* above gas modes
|
||||||
|
*
|
||||||
|
* @typedef {SendStateAmountModes[keyof SendStateAmountModes]} SendStateAmountModeStrings
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The modes that the amount field can be set by
|
* The modes that the amount field can be set by
|
||||||
* 1. INPUT - the user provides the amount by typing in the field
|
*
|
||||||
* 2. MAX - The user selects the MAX button and amount is calculated based on
|
* @type {SendStateAmountModes}
|
||||||
* balance - (amount + gasTotal)
|
|
||||||
*/
|
*/
|
||||||
export const AMOUNT_MODES = {
|
export const AMOUNT_MODES = {
|
||||||
INPUT: 'INPUT',
|
INPUT: 'INPUT',
|
||||||
MAX: 'MAX',
|
MAX: 'MAX',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SendStateRecipientModes
|
||||||
|
* @property {'MY_ACCOUNTS'} MY_ACCOUNTS - the user is displayed a list of
|
||||||
|
* their own accounts to send to.
|
||||||
|
* @property {'CONTACT_LIST'} CONTACT_LIST - The user is displayed a list of
|
||||||
|
* their contacts and addresses they have recently send to.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type will work anywhere you expect a string that can be one of the
|
||||||
|
* above recipient modes
|
||||||
|
*
|
||||||
|
* @typedef {SendStateRecipientModes[keyof SendStateRecipientModes]} SendStateRecipientModeStrings
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of recipient list that is displayed to user
|
||||||
|
*
|
||||||
|
* @type {SendStateRecipientModes}
|
||||||
|
*/
|
||||||
export const RECIPIENT_SEARCH_MODES = {
|
export const RECIPIENT_SEARCH_MODES = {
|
||||||
MY_ACCOUNTS: 'MY_ACCOUNTS',
|
MY_ACCOUNTS: 'MY_ACCOUNTS',
|
||||||
CONTACT_LIST: 'CONTACT_LIST',
|
CONTACT_LIST: 'CONTACT_LIST',
|
||||||
@ -429,8 +520,8 @@ export const computeEstimatedGasLimit = createAsyncThunk(
|
|||||||
* we receive a GWEI estimate from the controller, we still need to do this
|
* we receive a GWEI estimate from the controller, we still need to do this
|
||||||
* weird conversion to get the proper rounding.
|
* weird conversion to get the proper rounding.
|
||||||
*
|
*
|
||||||
* @param {T} gasPriceEstimate
|
* @param {string} gasPriceEstimate
|
||||||
* @returns
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function getRoundedGasPrice(gasPriceEstimate) {
|
function getRoundedGasPrice(gasPriceEstimate) {
|
||||||
const gasPriceInDecGwei = conversionUtil(gasPriceEstimate, {
|
const gasPriceInDecGwei = conversionUtil(gasPriceEstimate, {
|
||||||
@ -538,7 +629,7 @@ export const initializeSendState = createAsyncThunk(
|
|||||||
});
|
});
|
||||||
gasLimit = estimatedGasLimit || gasLimit;
|
gasLimit = estimatedGasLimit || gasLimit;
|
||||||
}
|
}
|
||||||
// We have to keep the gas slice in sync with the draft send transaction
|
// We have to keep the gas slice in sync with the send slice state
|
||||||
// so that it'll be initialized correctly if the gas modal is opened.
|
// so that it'll be initialized correctly if the gas modal is opened.
|
||||||
await thunkApi.dispatch(setCustomGasLimit(gasLimit));
|
await thunkApi.dispatch(setCustomGasLimit(gasLimit));
|
||||||
// We must determine the balance of the asset that the transaction will be
|
// We must determine the balance of the asset that the transaction will be
|
||||||
@ -585,96 +676,166 @@ export const initializeSendState = createAsyncThunk(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SendState
|
||||||
|
* @property {string} [id] - The id of a transaction that is being edited
|
||||||
|
* @property {SendStateStagesStrings} stage - The stage of the send flow that
|
||||||
|
* the user has progressed to. Defaults to 'INACTIVE' which results in the
|
||||||
|
* send screen not being shown.
|
||||||
|
* @property {SendStateStatusStrings} status - The status of the send slice
|
||||||
|
* which will be either 'VALID' or 'INVALID'
|
||||||
|
* @property {string} transactionType - Determines type of transaction being
|
||||||
|
* sent, defaulted to 0x0 (legacy).
|
||||||
|
* @property {boolean} eip1559support - tracks whether the current network
|
||||||
|
* supports EIP 1559 transactions.
|
||||||
|
* @property {Object} account - Details about the user's account.
|
||||||
|
* @property {string} [account.address] - from account address, defaults to
|
||||||
|
* selected account. will be the account the original transaction was sent
|
||||||
|
* from in the case of the EDIT stage.
|
||||||
|
* @property {string} [account.balance] - Hex string representing the balance
|
||||||
|
* of the from account.
|
||||||
|
* @property {string} [userInputHexData] - When a user has enabled custom hex
|
||||||
|
* data field in advanced options, they can supply data to the field which is
|
||||||
|
* stored under this key.
|
||||||
|
* @property {Object} gas - Details about the current gas settings
|
||||||
|
* @property {boolean} gas.isGasEstimateLoading - Indicates whether the gas
|
||||||
|
* estimate is loading.
|
||||||
|
* @property {string} [gas.gasEstimatePollToken] - String token identifying a
|
||||||
|
* listener for polling on the gasFeeController
|
||||||
|
* @property {boolean} gas.isCustomGasSet - true if the user set custom gas in
|
||||||
|
* the custom gas modal
|
||||||
|
* @property {string} gas.gasLimit - maximum gas needed for tx.
|
||||||
|
* @property {string} gas.gasPrice - price in wei to pay per gas.
|
||||||
|
* @property {string} gas.maxFeePerGas - Maximum price in wei to pay per gas.
|
||||||
|
* @property {string} gas.maxPriorityFeePerGas - Maximum priority fee in wei to
|
||||||
|
* pay per gas.
|
||||||
|
* @property {string} gas.gasPriceEstimate - Expected price in wei necessary to
|
||||||
|
* pay per gas used for a transaction to be included in a reasonable timeframe.
|
||||||
|
* Comes from the GasFeeController.
|
||||||
|
* @property {string} gas.gasTotal - maximum total price in wei to pay.
|
||||||
|
* @property {string} gas.minimumGasLimit - minimum supported gasLimit.
|
||||||
|
* @property {string} [gas.error] - error to display for gas fields.
|
||||||
|
* @property {Object} amount - An object containing information about the
|
||||||
|
* amount of currency to send.
|
||||||
|
* @property {SendStateAmountModeStrings} amount.mode - Describe whether the
|
||||||
|
* user has manually input an amount or if they have selected max to send the
|
||||||
|
* maximum amount of the selected currency.
|
||||||
|
* @property {string} amount.value - A hex string representing the amount of
|
||||||
|
* the selected currency to send.
|
||||||
|
* @property {string} [amount.error] - Error to display for the amount field.
|
||||||
|
* @property {Object} asset - An object that describes the asset that the user
|
||||||
|
* has selected to send.
|
||||||
|
* @property {AssetTypesString} asset.type - The type of asset that the user
|
||||||
|
* is attempting to send. Defaults to 'NATIVE' which represents the native
|
||||||
|
* asset of the chain. Can also be 'TOKEN' or 'COLLECTIBLE'.
|
||||||
|
* @property {string} asset.balance - A hex string representing the balance
|
||||||
|
* that the user holds of the asset that they are attempting to send.
|
||||||
|
* @property {Object} [asset.details] - An object that describes the selected
|
||||||
|
* asset in the case that the user is sending a token or collectibe. Will be
|
||||||
|
* null when asset.type is 'NATIVE'.
|
||||||
|
* @property {string} [asset.details.address] - The address of the selected
|
||||||
|
* 'TOKEN' or 'COLLECTIBLE' contract.
|
||||||
|
* @property {string} [asset.details.symbol] - The symbol of the selected
|
||||||
|
* asset.
|
||||||
|
* @property {number} [asset.details.decimals] - The number of decimals of the
|
||||||
|
* selected 'TOKEN' asset.
|
||||||
|
* @property {number} [asset.details.tokenId] - The id of the selected
|
||||||
|
* 'COLLECTIBLE' asset.
|
||||||
|
* @property {TokenStandardStrings} [asset.details.standard] - The standard
|
||||||
|
* of the selected 'TOKEN' or 'COLLECTIBLE' asset.
|
||||||
|
* @property {boolean} [asset.details.isERC721] - True when the asset is a
|
||||||
|
* ERC721 token.
|
||||||
|
* @property {string} [asset.error] - Error to display when there is an issue
|
||||||
|
* with the asset.
|
||||||
|
* @property {Object} recipient - An object that describes the intended
|
||||||
|
* recipient of the transaction.
|
||||||
|
* @property {SendStateRecipientModeStrings} recipient.mode - Describes which
|
||||||
|
* list of recipients the user is shown on the add recipient screen. When this
|
||||||
|
* key is set to 'MY_ACCOUNTS' the user is shown the list of accounts they
|
||||||
|
* own. When it is 'CONTACT_LIST' the user is shown the list of contacts they
|
||||||
|
* have saved in MetaMask and any addresses they have recently sent to.
|
||||||
|
* @property {string} recipient.address - The fully qualified address of the
|
||||||
|
* recipient. This is set after the recipient.userInput is validated, the
|
||||||
|
* userInput field is quickly updated to avoid delay between keystrokes and
|
||||||
|
* seeing the input field updated. After a debounc the address typed is
|
||||||
|
* validated and then the address field is updated. The address field is also
|
||||||
|
* set when the user selects a contact or account from the list, or an ENS
|
||||||
|
* resolution when typing ENS names.
|
||||||
|
* @property {string} recipient.userInput - The user input of the recipient
|
||||||
|
* which is updated quickly to avoid delays in the UI reflecting manual entry
|
||||||
|
* of addresses.
|
||||||
|
* @property {string} recipient.nickname - The nickname that the user has added
|
||||||
|
* to their address book for the recipient.address.
|
||||||
|
* @property {string} [recipient.error] - Error to display on the address field.
|
||||||
|
* @property {string} [recipient.warning] - Warning to display on the address
|
||||||
|
* field.
|
||||||
|
* @property {Object} multiLayerFees - An object containing attributes for use
|
||||||
|
* on chains that have layer 1 and layer 2 fees to consider for gas
|
||||||
|
* calculations.
|
||||||
|
* @property {string} multiLayerFees.layer1GasTotal - Layer 1 gas fee total on
|
||||||
|
* multi-layer fee networks
|
||||||
|
* @property {Array<{event: string, timestamp: number}>} history - An array of
|
||||||
|
* entries that describe the user's journey through the send flow. This is
|
||||||
|
* sent to the controller for attaching to state logs for troubleshooting and
|
||||||
|
* support.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {SendState}
|
||||||
|
*/
|
||||||
export const initialState = {
|
export const initialState = {
|
||||||
id: null,
|
id: null,
|
||||||
// which stage of the send flow is the user on
|
|
||||||
stage: SEND_STAGES.INACTIVE,
|
stage: SEND_STAGES.INACTIVE,
|
||||||
// status of the send slice, either VALID or INVALID
|
|
||||||
status: SEND_STATUSES.VALID,
|
status: SEND_STATUSES.VALID,
|
||||||
// Determines type of transaction being sent, defaulted to 0x0 (legacy)
|
|
||||||
transactionType: TRANSACTION_ENVELOPE_TYPES.LEGACY,
|
transactionType: TRANSACTION_ENVELOPE_TYPES.LEGACY,
|
||||||
// tracks whether the current network supports EIP 1559 transactions
|
|
||||||
eip1559support: false,
|
eip1559support: false,
|
||||||
account: {
|
account: {
|
||||||
// from account address, defaults to selected account. will be the account
|
|
||||||
// the original transaction was sent from in the case of the EDIT stage
|
|
||||||
address: null,
|
address: null,
|
||||||
// balance of the from account
|
|
||||||
balance: '0x0',
|
balance: '0x0',
|
||||||
},
|
},
|
||||||
userInputHexData: null,
|
userInputHexData: null,
|
||||||
gas: {
|
gas: {
|
||||||
// indicate whether the gas estimate is loading
|
|
||||||
isGasEstimateLoading: true,
|
isGasEstimateLoading: true,
|
||||||
// String token identifying a listener for polling on the gasFeeController
|
|
||||||
gasEstimatePollToken: null,
|
gasEstimatePollToken: null,
|
||||||
// has the user set custom gas in the custom gas modal
|
|
||||||
isCustomGasSet: false,
|
isCustomGasSet: false,
|
||||||
// maximum gas needed for tx
|
|
||||||
gasLimit: '0x0',
|
gasLimit: '0x0',
|
||||||
// price in wei to pay per gas
|
|
||||||
gasPrice: '0x0',
|
gasPrice: '0x0',
|
||||||
// maximum price in wei to pay per gas
|
|
||||||
maxFeePerGas: '0x0',
|
maxFeePerGas: '0x0',
|
||||||
// maximum priority fee in wei to pay per gas
|
|
||||||
maxPriorityFeePerGas: '0x0',
|
maxPriorityFeePerGas: '0x0',
|
||||||
// expected price in wei necessary to pay per gas used for a transaction
|
|
||||||
// to be included in a reasonable timeframe. Comes from GasFeeController.
|
|
||||||
gasPriceEstimate: '0x0',
|
gasPriceEstimate: '0x0',
|
||||||
// maximum total price in wei to pay
|
|
||||||
gasTotal: '0x0',
|
gasTotal: '0x0',
|
||||||
// minimum supported gasLimit
|
|
||||||
minimumGasLimit: GAS_LIMITS.SIMPLE,
|
minimumGasLimit: GAS_LIMITS.SIMPLE,
|
||||||
// error to display for gas fields
|
|
||||||
error: null,
|
error: null,
|
||||||
},
|
},
|
||||||
amount: {
|
amount: {
|
||||||
// The mode to use when determining new amounts. For INPUT mode the
|
|
||||||
// provided payload is always used. For MAX it is calculated based on avail
|
|
||||||
// asset balance
|
|
||||||
mode: AMOUNT_MODES.INPUT,
|
mode: AMOUNT_MODES.INPUT,
|
||||||
// Current value of the transaction, how much of the asset are we sending
|
|
||||||
value: '0x0',
|
value: '0x0',
|
||||||
// error to display for amount field
|
|
||||||
error: null,
|
error: null,
|
||||||
},
|
},
|
||||||
asset: {
|
asset: {
|
||||||
// type can be either NATIVE such as ETH or TOKEN for ERC20 tokens
|
|
||||||
type: ASSET_TYPES.NATIVE,
|
type: ASSET_TYPES.NATIVE,
|
||||||
// the balance the user holds at the from address for this asset
|
|
||||||
balance: '0x0',
|
balance: '0x0',
|
||||||
// In the case of tokens, the address, decimals and symbol of the token
|
|
||||||
// will be included in details
|
|
||||||
details: null,
|
details: null,
|
||||||
// error to display when there is an issue with the asset
|
|
||||||
error: null,
|
error: null,
|
||||||
},
|
},
|
||||||
recipient: {
|
recipient: {
|
||||||
// Defines which mode to use for searching for matches in the input field
|
|
||||||
mode: RECIPIENT_SEARCH_MODES.CONTACT_LIST,
|
mode: RECIPIENT_SEARCH_MODES.CONTACT_LIST,
|
||||||
// Partial, not yet validated, entry into the address field. Used to share
|
|
||||||
// user input amongst the AddRecipient and EnsInput components.
|
|
||||||
userInput: '',
|
userInput: '',
|
||||||
// The address of the recipient
|
|
||||||
address: '',
|
address: '',
|
||||||
// The nickname stored in the user's address book for the recipient address
|
|
||||||
nickname: '',
|
nickname: '',
|
||||||
// Error to display on the address field
|
|
||||||
error: null,
|
error: null,
|
||||||
// Warning to display on the address field
|
|
||||||
warning: null,
|
warning: null,
|
||||||
},
|
},
|
||||||
multiLayerFees: {
|
multiLayerFees: {
|
||||||
// Layer 1 gas fee total on multi-layer fee networks
|
|
||||||
layer1GasTotal: '0x0',
|
layer1GasTotal: '0x0',
|
||||||
},
|
},
|
||||||
history: [],
|
history: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a txParams from the send state
|
* Generates a txParams from the send slice.
|
||||||
*
|
*
|
||||||
* @param {Object} state - the Send slice state
|
* @param {SendState} state - the Send slice state
|
||||||
* @returns {import(
|
* @returns {import(
|
||||||
* '../../../shared/constants/transaction'
|
* '../../../shared/constants/transaction'
|
||||||
* ).TxParams} A txParams object that can be used to create a transaction or
|
* ).TxParams} A txParams object that can be used to create a transaction or
|
||||||
@ -765,8 +926,10 @@ const slice = createSlice({
|
|||||||
* update current amount.value in state and run post update validation of
|
* update current amount.value in state and run post update validation of
|
||||||
* the amount field and the send state.
|
* the amount field and the send state.
|
||||||
*
|
*
|
||||||
* @param state
|
* @param {SendStateDraft} state - A writable draft of the send state to be
|
||||||
* @param action
|
* updated.
|
||||||
|
* @param {import('@reduxjs/toolkit').PayloadAction<string>} action - The
|
||||||
|
* hex string to be set as the amount value.
|
||||||
*/
|
*/
|
||||||
updateSendAmount: (state, action) => {
|
updateSendAmount: (state, action) => {
|
||||||
state.amount.value = addHexPrefix(action.payload);
|
state.amount.value = addHexPrefix(action.payload);
|
||||||
@ -787,7 +950,8 @@ const slice = createSlice({
|
|||||||
* the updateSendAmount action above with the computed value, which will
|
* the updateSendAmount action above with the computed value, which will
|
||||||
* revalidate the field and form.
|
* revalidate the field and form.
|
||||||
*
|
*
|
||||||
* @param state
|
* @param {SendStateDraft} state - A writable draft of the send state to be
|
||||||
|
* updated.
|
||||||
*/
|
*/
|
||||||
updateAmountToMax: (state) => {
|
updateAmountToMax: (state) => {
|
||||||
let amount = '0x0';
|
let amount = '0x0';
|
||||||
@ -822,19 +986,45 @@ const slice = createSlice({
|
|||||||
/**
|
/**
|
||||||
* updates the userInputHexData state key
|
* updates the userInputHexData state key
|
||||||
*
|
*
|
||||||
* @param state
|
* @param {SendStateDraft} state - A writable draft of the send state to be
|
||||||
* @param action
|
* updated.
|
||||||
|
* @param {import('@reduxjs/toolkit').PayloadAction<string>} action - The
|
||||||
|
* hex string to be set as the userInputHexData value.
|
||||||
*/
|
*/
|
||||||
updateUserInputHexData: (state, action) => {
|
updateUserInputHexData: (state, action) => {
|
||||||
state.userInputHexData = action.payload;
|
state.userInputHexData = action.payload;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Transaction details of a previously created transaction that the user
|
||||||
|
* has selected to edit.
|
||||||
|
*
|
||||||
|
* @typedef {Object} EditTransactionPayload
|
||||||
|
* @property {string} gasLimit - The hex string maximum gas to use.
|
||||||
|
* @property {string} gasPrice - The amount in wei to pay for gas, in hex
|
||||||
|
* format.
|
||||||
|
* @property {string} amount - The amount of the currency to send, in hex
|
||||||
|
* format.
|
||||||
|
* @property {string} address - The address to send the transaction to.
|
||||||
|
* @property {string} [nickname] - The nickname the user has associated
|
||||||
|
* with the address in their contact book.
|
||||||
|
* @property {string} id - The id of the transaction in the
|
||||||
|
* TransactionController state[
|
||||||
|
* @property {string} from - the address that the user is sending from
|
||||||
|
* @property {string} [data] - The hex data that describes the transaction.
|
||||||
|
* Used primarily for contract interactions, like token sends, but can
|
||||||
|
* also be provided by the user.
|
||||||
|
*/
|
||||||
/**
|
/**
|
||||||
* Initiates the edit transaction flow by setting the stage to 'EDIT' and
|
* Initiates the edit transaction flow by setting the stage to 'EDIT' and
|
||||||
* then pulling the details of the previously submitted transaction from
|
* then pulling the details of the previously submitted transaction from
|
||||||
* the action payload.
|
* the action payload.
|
||||||
*
|
*
|
||||||
* @param state
|
* @param {SendStateDraft} state - A writable draft of the send state to be
|
||||||
* @param action
|
* updated.
|
||||||
|
* @param {import(
|
||||||
|
* '@reduxjs/toolkit'
|
||||||
|
* ).PayloadAction<EditTransactionPayload>} action - The details of the
|
||||||
|
* transaction to be edited.
|
||||||
*/
|
*/
|
||||||
editTransaction: (state, action) => {
|
editTransaction: (state, action) => {
|
||||||
state.stage = SEND_STAGES.EDIT;
|
state.stage = SEND_STAGES.EDIT;
|
||||||
@ -855,9 +1045,10 @@ const slice = createSlice({
|
|||||||
* recomputes the maximum amount if the current amount mode is 'MAX' and
|
* recomputes the maximum amount if the current amount mode is 'MAX' and
|
||||||
* sending the native token. ERC20 assets max amount is unaffected by
|
* sending the native token. ERC20 assets max amount is unaffected by
|
||||||
* gasTotal so does not need to be recomputed. Finally, validates the gas
|
* gasTotal so does not need to be recomputed. Finally, validates the gas
|
||||||
* field and send state, then updates the draft transaction.
|
* field and send state.
|
||||||
*
|
*
|
||||||
* @param state
|
* @param {SendStateDraft} state - A writable draft of the send state to be
|
||||||
|
* updated.
|
||||||
*/
|
*/
|
||||||
calculateGasTotal: (state) => {
|
calculateGasTotal: (state) => {
|
||||||
// use maxFeePerGas as the multiplier if working with a FEE_MARKET transaction
|
// use maxFeePerGas as the multiplier if working with a FEE_MARKET transaction
|
||||||
@ -885,19 +1076,37 @@ const slice = createSlice({
|
|||||||
/**
|
/**
|
||||||
* sets the provided gasLimit in state and then recomputes the gasTotal.
|
* sets the provided gasLimit in state and then recomputes the gasTotal.
|
||||||
*
|
*
|
||||||
* @param state
|
* @param {SendStateDraft} state - A writable draft of the send state to be
|
||||||
* @param action
|
* updated.
|
||||||
|
* @param {import('@reduxjs/toolkit').PayloadAction<string>} action - The
|
||||||
|
* gasLimit in hex to set in state.
|
||||||
*/
|
*/
|
||||||
updateGasLimit: (state, action) => {
|
updateGasLimit: (state, action) => {
|
||||||
state.gas.gasLimit = addHexPrefix(action.payload);
|
state.gas.gasLimit = addHexPrefix(action.payload);
|
||||||
slice.caseReducers.calculateGasTotal(state);
|
slice.caseReducers.calculateGasTotal(state);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @typedef {Object} GasFeeUpdatePayload
|
||||||
|
* @property {TransactionTypeString} transactionType - The transaction type
|
||||||
|
* @property {string} [maxFeePerGas] - The maximum amount in hex wei to pay
|
||||||
|
* per gas on a FEE_MARKET transaction.
|
||||||
|
* @property {string} [maxPriorityFeePerGas] - The maximum amount in hex
|
||||||
|
* wei to pay per gas as an incentive to miners on a FEE_MARKET
|
||||||
|
* transaction.
|
||||||
|
* @property {string} [gasPrice] - The amount in hex wei to pay per gas on
|
||||||
|
* a LEGACY transaction.
|
||||||
|
* @property {boolean} [isAutomaticUpdate] - true if the update is the
|
||||||
|
* result of a gas estimate update from the controller.
|
||||||
|
*/
|
||||||
/**
|
/**
|
||||||
* Sets the appropriate gas fees in state and determines and sets the
|
* Sets the appropriate gas fees in state and determines and sets the
|
||||||
* appropriate transactionType based on gas fee fields received.
|
* appropriate transactionType based on gas fee fields received.
|
||||||
*
|
*
|
||||||
* @param state
|
* @param {SendStateDraft} state - A writable draft of the send state to be
|
||||||
* @param action
|
* updated.
|
||||||
|
* @param {import(
|
||||||
|
* '@reduxjs/toolkit'
|
||||||
|
* ).PayloadAction<GasFeeUpdatePayload>} action
|
||||||
*/
|
*/
|
||||||
updateGasFees: (state, action) => {
|
updateGasFees: (state, action) => {
|
||||||
if (
|
if (
|
||||||
@ -929,11 +1138,22 @@ const slice = createSlice({
|
|||||||
}
|
}
|
||||||
slice.caseReducers.calculateGasTotal(state);
|
slice.caseReducers.calculateGasTotal(state);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @typedef {Object} GasEstimateUpdatePayload
|
||||||
|
* @property {GasEstimateType} gasEstimateType - The type of gas estimation
|
||||||
|
* provided by the controller.
|
||||||
|
* @property {(
|
||||||
|
* EthGasPriceEstimate | LegacyGasPriceEstimate | GasFeeEstimates
|
||||||
|
* )} gasFeeEstimates - The gas fee estimates provided by the controller.
|
||||||
|
*/
|
||||||
/**
|
/**
|
||||||
* Sets the appropriate gas fees in state after receiving new estimates.
|
* Sets the appropriate gas fees in state after receiving new estimates.
|
||||||
*
|
*
|
||||||
* @param state
|
* @param {SendStateDraft} state - A writable draft of the send state to be
|
||||||
* @param action
|
* updated.
|
||||||
|
* @param {(
|
||||||
|
* import('@reduxjs/toolkit').PayloadAction<GasEstimateUpdatePayload
|
||||||
|
* )} action - The gas fee update payload
|
||||||
*/
|
*/
|
||||||
updateGasFeeEstimates: (state, action) => {
|
updateGasFeeEstimates: (state, action) => {
|
||||||
const { gasFeeEstimates, gasEstimateType } = action.payload;
|
const { gasFeeEstimates, gasEstimateType } = action.payload;
|
||||||
@ -982,8 +1202,10 @@ const slice = createSlice({
|
|||||||
/**
|
/**
|
||||||
* sets the layer 1 fees total (for a multi-layer fee network)
|
* sets the layer 1 fees total (for a multi-layer fee network)
|
||||||
*
|
*
|
||||||
* @param state
|
* @param {SendStateDraft} state - A writable draft of the send state to be
|
||||||
* @param action
|
* updated.
|
||||||
|
* @param {import('@reduxjs/toolkit').PayloadAction<string>} action - the
|
||||||
|
* layer1GasTotal to set in hex wei.
|
||||||
*/
|
*/
|
||||||
updateLayer1Fees: (state, action) => {
|
updateLayer1Fees: (state, action) => {
|
||||||
state.multiLayerFees.layer1GasTotal = action.payload;
|
state.multiLayerFees.layer1GasTotal = action.payload;
|
||||||
@ -998,8 +1220,12 @@ const slice = createSlice({
|
|||||||
* sets the amount mode to the provided value as long as it is one of the
|
* sets the amount mode to the provided value as long as it is one of the
|
||||||
* supported modes (MAX|INPUT)
|
* supported modes (MAX|INPUT)
|
||||||
*
|
*
|
||||||
* @param state
|
* @param {SendStateDraft} state - A writable draft of the send state to be
|
||||||
* @param action
|
* updated.
|
||||||
|
* @param {import(
|
||||||
|
* '@reduxjs/toolkit'
|
||||||
|
* ).PayloadAction<SendStateAmountModeStrings>} action - The amount mode
|
||||||
|
* to set the state to.
|
||||||
*/
|
*/
|
||||||
updateAmountMode: (state, action) => {
|
updateAmountMode: (state, action) => {
|
||||||
if (Object.values(AMOUNT_MODES).includes(action.payload)) {
|
if (Object.values(AMOUNT_MODES).includes(action.payload)) {
|
||||||
@ -1051,9 +1277,9 @@ const slice = createSlice({
|
|||||||
// to the ADD_RECIPIENT stage.
|
// to the ADD_RECIPIENT stage.
|
||||||
state.stage = SEND_STAGES.ADD_RECIPIENT;
|
state.stage = SEND_STAGES.ADD_RECIPIENT;
|
||||||
} else {
|
} else {
|
||||||
// if an address is provided and an id exists on the draft transaction,
|
// if an address is provided and an id exists, we progress to the EDIT
|
||||||
// we progress to the EDIT stage, otherwise we progress to the DRAFT
|
// stage, otherwise we progress to the DRAFT stage. We also reset the
|
||||||
// stage. We also reset the search mode for recipient search.
|
// search mode for recipient search.
|
||||||
state.stage = state.id === null ? SEND_STAGES.DRAFT : SEND_STAGES.EDIT;
|
state.stage = state.id === null ? SEND_STAGES.DRAFT : SEND_STAGES.EDIT;
|
||||||
state.recipient.mode = RECIPIENT_SEARCH_MODES.CONTACT_LIST;
|
state.recipient.mode = RECIPIENT_SEARCH_MODES.CONTACT_LIST;
|
||||||
}
|
}
|
||||||
@ -1508,7 +1734,8 @@ export function updateSendAsset({ type, details }) {
|
|||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
process.env.COLLECTIBLES_V1 &&
|
process.env.COLLECTIBLES_V1 &&
|
||||||
(standard === ERC721 || standard === ERC1155)
|
(standard === TOKEN_STANDARDS.ERC721 ||
|
||||||
|
standard === TOKEN_STANDARDS.ERC1155)
|
||||||
) {
|
) {
|
||||||
await dispatch(hideLoadingIndication());
|
await dispatch(hideLoadingIndication());
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -1530,7 +1757,7 @@ export function updateSendAsset({ type, details }) {
|
|||||||
// the currently active account. In addition its possible for the balance
|
// the currently active account. In addition its possible for the balance
|
||||||
// check to take a decent amount of time, so we display a loading
|
// check to take a decent amount of time, so we display a loading
|
||||||
// indication so that that immediate feedback is displayed to the user.
|
// indication so that that immediate feedback is displayed to the user.
|
||||||
if (details.standard === ERC20) {
|
if (details.standard === TOKEN_STANDARDS.ERC20) {
|
||||||
error = null;
|
error = null;
|
||||||
balance = await getERC20Balance(details, userAddress);
|
balance = await getERC20Balance(details, userAddress);
|
||||||
}
|
}
|
||||||
@ -1562,7 +1789,7 @@ export function updateSendAsset({ type, details }) {
|
|||||||
details.standard = standard;
|
details.standard = standard;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (details.standard === ERC1155) {
|
if (details.standard === TOKEN_STANDARDS.ERC1155) {
|
||||||
throw new Error('Sends of ERC1155 tokens are not currently supported');
|
throw new Error('Sends of ERC1155 tokens are not currently supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,28 @@ export const ERC20 = 'ERC20';
|
|||||||
export const ERC721 = 'ERC721';
|
export const ERC721 = 'ERC721';
|
||||||
export const ERC1155 = 'ERC1155';
|
export const ERC1155 = 'ERC1155';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} TokenStandards
|
||||||
|
* @property {'ERC20'} ERC20 - A token that conforms to the ERC20 standard.
|
||||||
|
* @property {'ERC721'} ERC721 - A token that conforms to the ERC721 standard.
|
||||||
|
* @property {'ERC1155'} ERC1155 - A token that conforms to the ERC1155
|
||||||
|
* standard.
|
||||||
|
* @property {'NONE'} NONE - Not a token, but rather the base asset of the
|
||||||
|
* selected chain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type will work anywhere you expect a string that can be one of the
|
||||||
|
* above statuses
|
||||||
|
*
|
||||||
|
* @typedef {TokenStandards[keyof TokenStandards]} TokenStandardStrings
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the standard which a token conforms to.
|
||||||
|
*
|
||||||
|
* @type {TokenStandards}
|
||||||
|
*/
|
||||||
export const TOKEN_STANDARDS = {
|
export const TOKEN_STANDARDS = {
|
||||||
ERC20,
|
ERC20,
|
||||||
ERC721,
|
ERC721,
|
||||||
|
Loading…
Reference in New Issue
Block a user