This repository was archived by the owner on Nov 10, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 357
(Feature) - #1048 Tx will fail warning #1675
Merged
Merged
Changes from all commits
Commits
Show all changes
111 commits
Select commit
Hold shift + click to select a range
18508c3
Makes getGasEstimationTxResponse exportable
Agupane 34749fb
Add check for failing txs on approveTxModal
Agupane 5baebb7
Adds styles for reviewTx
Agupane 5a676b7
Adds useTxSuccessCheck hook
Agupane 6dd2639
Remove hook
Agupane f614c3c
Adds checkIfTxWillFail function
Agupane a73b764
Uses checkIfTxWillFailAsync on reviewTx modal
Agupane 0452997
Improves approveTx modal
Agupane 1661be8
Add check for failing transaction in contract interaction modal
Agupane b3d92e6
Add check for reviewCollectible
Agupane 5d97475
Fix check on sendFunds reviewTx
Agupane 89253ab
Adds styling for contractInteraction modal
Agupane 117e94a
Fix gas calculation for native token transfers
Agupane 73cc229
Remove logs
Agupane 94b5c03
Merge branch 'development' into feature/1048-tx-will-fail-warning
Agupane 5922302
Rename estimateDataGasCosts to parseRequiredTxGasResponse
Agupane 977d026
Refactor checkIfExecTxWill usage
Agupane 1652773
Refactor checkIfTxWillFailAsync in ReviewTx
Agupane 9a689f5
Use getPreValidatedSignatures in createTransaction()
Agupane f74e516
Refactor estimateTxGasCosts
Agupane a7011bf
Refactor ReviewTx: extract useEffects to hooks
Agupane 528aea7
Merge branch 'development' into feature/1048-tx-will-fail-warning
c69ffca
Merge branch 'development' into feature/1048-tx-will-fail-warning
Agupane 60baeae
Remove unnecessary gas transfer amount
Agupane 4b0db6b
Refactor estimateTxGasCosts: extract checkIfTxIsExecution and estimat…
Agupane 72680d4
Fix tx amount
Agupane e6b361c
Moves useCheckIfTransactionWillFail to logic/hooks folder
Agupane 402ed43
Replaces useEffect usage with useCheckIfTransactionWillFail hook
Agupane e100113
Improves modal's wording
Agupane c716cef
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane 49641a4
Remove comment
Agupane c184d3b
Merge branch 'development' into feature/1048-tx-will-fail-warning
fernandomg 1a2bfc8
Merge branch 'development' into feature/1048-tx-will-fail-warning
fernandomg eaec906
Merge branch 'development' into feature/1048-tx-will-fail-warning
Agupane 6c98251
Merge branch 'development' into feature/1048-tx-will-fail-warning
Agupane 983551f
Fix error parsing the cancel transaction error message from GETH nodes
Agupane 0c0a106
Remove useCheckIfTransactionWillFail
Agupane 54bd701
Replace useCheckIfTransactionWillFail from modals with useEstimateTra…
Agupane 4ea2553
Replace estimateGasCosts from every review tx modal with useEstimateT…
Agupane a6685cc
Replace estimateGasCosts from every review tx modal with useEstimateT…
Agupane a8a3e21
Extract isExecution calculation to useEstimateTransactionGas
Agupane c461954
Creates TransactionFailText
Agupane 21fbeda
Uses TransactionFailText in the review modals
Agupane ad27d74
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane 343e1f0
Remove unnecessary styles
Agupane 0c208b7
Fix imports
Agupane b162729
Remove css
Agupane 74abb25
Fix missing style
Agupane d42cdc4
Remove duplicated function
Agupane e57ac46
Merge branch 'development' into feature/1048-tx-will-fail-warning
Agupane 7a7bfc7
Merge branch 'development' into feature/1048-tx-will-fail-warning
fae6a16
Fix modal height
Agupane da08f32
Fix wrong selector usage
Agupane 220278c
Merge branch 'feature/1048-tx-will-fail-warning' of https://github.co…
Agupane 3df879b
Fix missing null check on cancel tx confirmations
Agupane da1d5d8
Add guard for CLOSE_SNACKBAR action when tx was already dismissed
Agupane 00ccae2
Improves useEstimateTransactionGas in review custom tx and contract i…
Agupane 513e6cb
Merge branch 'release/v2.17.0' of https://github.com/gnosis/safe-reac…
Agupane 0299c03
Merge branch 'development' into feature/1048-tx-will-fail-warning
6346317
Fix hook dependency
Agupane e8d73be
Merge branch 'feature/1048-tx-will-fail-warning' of https://github.co…
Agupane abe70f2
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane fcfd8bb
Fix review replace/remove/add owner modals styling
Agupane 7f7c638
Merge branch 'development' into feature/1048-tx-will-fail-warning
Agupane 1b2815b
Refactor response of useEstimateTransactionGas
Agupane ccf8a4e
Remove safeAddress as param to the useEstimateTransactionGas
Agupane 0742742
Improves how threshold is obtained in useEstimateTransactionGas.tsx
Agupane 0f739ef
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane a10bb19
Merge branch 'feature/1048-tx-will-fail-warning' of https://github.c…
Agupane 9d498b1
Rename gasCostHumanReadable to gasCostFormatted
Agupane e60730d
Add operation to useEstimateTransactionGas
Agupane 7ceda35
Refactor ConfirmTransactionModal to use useEstimateTransactionGas
Agupane f768d4c
Refactor proccessTransaction to use getPreValidatedSignatures method
Agupane 694e450
Fix default export of ApproveTxModal
Agupane 2c5f21d
Rename estimateExecTransactionGas to estimateGasForTransactionCreation
Agupane 40ec65d
Make estimateGasForTransactionCreation throw error instead of 0 gas
Agupane a1ac679
Adds estimateGasForTransactionExecution and estimateGasForTransaction…
Agupane 25bbd33
Move estimateTransactionGas to useEstimateTransactionGas
Agupane 316240b
Merge branch 'development' into feature/1048-tx-will-fail-warning
Agupane 7895924
Type and refactor generateSignaturesFromTxConfirmations
Agupane d8d64c9
Uses confirmations to estimateGasForTransactionExecution
Agupane 88d36f8
Adds TransactionFeesText component
Agupane 6550458
Update text
Agupane 53ef32a
Update text
Agupane dafe620
Remove unnecessary condition
Agupane 7c6ba58
Pass more parameters to estimateGasForTransactionExecution
Agupane 9c5bb19
Merge with 1048-tx-will-fail-warning
Agupane c6efa91
Removes unnecessary parameter in getNewTxNonce
Agupane f9fb706
Moves checkIfOffChainSignatureIsPossible to safeTxSigner.ts
Agupane e0b3af6
Fix check for null confirmations
Agupane d819531
Uses checkIfOffChainSignatureIsPossible on createTransaction.ts
Agupane 4ab94ac
Move TransactionFailText inside TransactionFees component
Agupane 1f06259
Remove unnecessary awaits
Agupane bee96c5
Pass safeTxGas to useEstimateTransactionGas.tsx
Agupane 63cab3f
Fix gas iteration on estimateGasForTransactionExecution
Agupane 00919c7
Fix estimateGasForTransactionExecution calculation
Agupane d25e907
Fix generateSignaturesFromTxConfirmations calculation
Agupane fccd8d1
Remove unnecessary Promise and await
289b6e2
Fix estimateGasForTransactionExecution for preApproving owner case
Agupane b8d0878
Merge branch 'feature/refactor-gas-calculation' of https://github.com…
Agupane 7ca4b64
Improve logging
Agupane 7f29a64
Merge branch 'feature/1048-tx-will-fail-warning' into feature/refacto…
Agupane 71fb956
Remove log
Agupane db15da9
Fix typo
Agupane b02f929
Uses operation in useEstimateTransactionGas
Agupane 40dabdd
Uses operation in useEstimateTransactionGas
Agupane 64d4fc3
Merge branch 'feature/1048-tx-will-fail-warning' into feature/refacto…
Agupane 0a77aa3
Fix missing dependency
Agupane 0f3b305
Merge pull request #1756 from gnosis/feature/refactor-gas-calculation
74aeebb
Merge branch 'development' into feature/1048-tx-will-fail-warning
Agupane 4386c3b
Merge branch 'development' into feature/1048-tx-will-fail-warning
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import { createStyles, makeStyles } from '@material-ui/core' | ||
| import { sm } from 'src/theme/variables' | ||
| import { EstimationStatus } from 'src/logic/hooks/useEstimateTransactionGas' | ||
| import Row from 'src/components/layout/Row' | ||
| import Paragraph from 'src/components/layout/Paragraph' | ||
| import Img from 'src/components/layout/Img' | ||
| import InfoIcon from 'src/assets/icons/info_red.svg' | ||
| import React from 'react' | ||
| import { useSelector } from 'react-redux' | ||
| import { safeThresholdSelector } from 'src/logic/safe/store/selectors' | ||
|
|
||
| const styles = createStyles({ | ||
| executionWarningRow: { | ||
| display: 'flex', | ||
| alignItems: 'center', | ||
| }, | ||
| warningIcon: { | ||
| marginRight: sm, | ||
| }, | ||
| }) | ||
|
|
||
| const useStyles = makeStyles(styles) | ||
|
|
||
| type TransactionFailTextProps = { | ||
| txEstimationExecutionStatus: EstimationStatus | ||
| isExecution: boolean | ||
| } | ||
|
|
||
| export const TransactionFailText = ({ | ||
| txEstimationExecutionStatus, | ||
| isExecution, | ||
| }: TransactionFailTextProps): React.ReactElement | null => { | ||
| const classes = useStyles() | ||
| const threshold = useSelector(safeThresholdSelector) | ||
|
|
||
| if (txEstimationExecutionStatus !== EstimationStatus.FAILURE) { | ||
| return null | ||
| } | ||
|
|
||
| let errorMessage = 'To save gas costs, avoid creating the transaction.' | ||
| if (isExecution) { | ||
| errorMessage = | ||
| threshold && threshold > 1 | ||
| ? `To save gas costs, cancel this transaction` | ||
| : `To save gas costs, avoid executing the transaction.` | ||
| } | ||
|
|
||
| return ( | ||
| <Row align="center"> | ||
| <Paragraph color="error" className={classes.executionWarningRow}> | ||
| <Img alt="Info Tooltip" height={16} src={InfoIcon} className={classes.warningIcon} /> | ||
| This transaction will most likely fail. {errorMessage} | ||
| </Paragraph> | ||
| </Row> | ||
| ) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import React from 'react' | ||
| import { EstimationStatus } from 'src/logic/hooks/useEstimateTransactionGas' | ||
| import Paragraph from 'src/components/layout/Paragraph' | ||
| import { getNetworkInfo } from 'src/config' | ||
| import { TransactionFailText } from 'src/components/TransactionFailText' | ||
|
|
||
| type TransactionFailTextProps = { | ||
| txEstimationExecutionStatus: EstimationStatus | ||
| gasCostFormatted: string | ||
| isExecution: boolean | ||
| isCreation: boolean | ||
| isOffChainSignature: boolean | ||
| } | ||
| const { nativeCoin } = getNetworkInfo() | ||
|
|
||
| export const TransactionFees = ({ | ||
| gasCostFormatted, | ||
| isExecution, | ||
| isCreation, | ||
| isOffChainSignature, | ||
| txEstimationExecutionStatus, | ||
| }: TransactionFailTextProps): React.ReactElement | null => { | ||
| let transactionAction | ||
| if (isCreation) { | ||
| transactionAction = 'create' | ||
| } else if (isExecution) { | ||
| transactionAction = 'execute' | ||
| } else { | ||
| transactionAction = 'approve' | ||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| <Paragraph> | ||
| You're about to {transactionAction} a transaction and will have to confirm it with your currently connected | ||
| wallet. | ||
| {!isOffChainSignature && | ||
| ` Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} | ||
| </Paragraph> | ||
| <TransactionFailText txEstimationExecutionStatus={txEstimationExecutionStatus} isExecution={isExecution} /> | ||
| </> | ||
| ) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,241 @@ | ||
| import { useEffect, useState } from 'react' | ||
| import { | ||
| estimateGasForTransactionApproval, | ||
| estimateGasForTransactionCreation, | ||
| estimateGasForTransactionExecution, | ||
| } from 'src/logic/safe/transactions/gas' | ||
| import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue' | ||
| import { formatAmount } from 'src/logic/tokens/utils/formatAmount' | ||
| import { calculateGasPrice } from 'src/logic/wallets/ethTransactions' | ||
| import { getNetworkInfo } from 'src/config' | ||
| import { useSelector } from 'react-redux' | ||
| import { | ||
| safeCurrentVersionSelector, | ||
| safeParamAddressFromStateSelector, | ||
| safeThresholdSelector, | ||
| } from 'src/logic/safe/store/selectors' | ||
| import { CALL } from 'src/logic/safe/transactions' | ||
| import { providerSelector } from '../wallets/store/selectors' | ||
|
|
||
| import { List } from 'immutable' | ||
| import { Confirmation } from 'src/logic/safe/store/models/types/confirmation' | ||
| import { checkIfOffChainSignatureIsPossible } from 'src/logic/safe/safeTxSigner' | ||
| import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' | ||
|
|
||
| export enum EstimationStatus { | ||
| LOADING = 'LOADING', | ||
| FAILURE = 'FAILURE', | ||
| SUCCESS = 'SUCCESS', | ||
| } | ||
|
|
||
| const checkIfTxIsExecution = (threshold: number, preApprovingOwner?: string, txConfirmations?: number): boolean => | ||
| txConfirmations === threshold || !!preApprovingOwner || threshold === 1 | ||
nicosampler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| const checkIfTxIsApproveAndExecution = (threshold: number, txConfirmations: number): boolean => | ||
| txConfirmations + 1 === threshold | ||
|
|
||
| const checkIfTxIsCreation = (txConfirmations: number): boolean => txConfirmations === 0 | ||
|
|
||
| type TransactionEstimationProps = { | ||
| txData: string | ||
| safeAddress: string | ||
| txRecipient: string | ||
| txConfirmations?: List<Confirmation> | ||
| txAmount?: string | ||
| operation?: number | ||
| gasPrice?: string | ||
| gasToken?: string | ||
| refundReceiver?: string // Address of receiver of gas payment (or 0 if tx.origin). | ||
| safeTxGas?: number | ||
| from?: string | ||
| isExecution: boolean | ||
| isCreation: boolean | ||
| isOffChainSignature?: boolean | ||
| approvalAndExecution?: boolean | ||
| } | ||
|
|
||
| const estimateTransactionGas = async ({ | ||
| txData, | ||
| safeAddress, | ||
| txRecipient, | ||
| txConfirmations, | ||
| txAmount, | ||
| operation, | ||
| gasPrice, | ||
| gasToken, | ||
| refundReceiver, | ||
| safeTxGas, | ||
| from, | ||
| isExecution, | ||
| isCreation, | ||
| isOffChainSignature = false, | ||
| approvalAndExecution, | ||
| }: TransactionEstimationProps): Promise<number> => { | ||
| if (isCreation) { | ||
| return estimateGasForTransactionCreation(safeAddress, txData, txRecipient, txAmount || '0', operation || CALL) | ||
| } | ||
|
|
||
| if (!from) { | ||
| throw new Error('No from provided for approving or execute transaction') | ||
| } | ||
|
|
||
| if (isExecution) { | ||
| return estimateGasForTransactionExecution({ | ||
| safeAddress, | ||
| txRecipient, | ||
| txConfirmations, | ||
| txAmount: txAmount || '0', | ||
| txData, | ||
| operation: operation || CALL, | ||
| from, | ||
| gasPrice: gasPrice || '0', | ||
| gasToken: gasToken || ZERO_ADDRESS, | ||
| refundReceiver: refundReceiver || ZERO_ADDRESS, | ||
| safeTxGas: safeTxGas || 0, | ||
| approvalAndExecution, | ||
| }) | ||
| } | ||
|
|
||
| return estimateGasForTransactionApproval({ | ||
| safeAddress, | ||
| operation: operation || CALL, | ||
| txData, | ||
| txAmount: txAmount || '0', | ||
| txRecipient, | ||
| from, | ||
| isOffChainSignature, | ||
| }) | ||
| } | ||
|
|
||
| type UseEstimateTransactionGasProps = { | ||
| txData: string | ||
| txRecipient: string | ||
| txConfirmations?: List<Confirmation> | ||
| txAmount?: string | ||
| preApprovingOwner?: string | ||
| operation?: number | ||
| safeTxGas?: number | ||
| } | ||
|
|
||
| type TransactionGasEstimationResult = { | ||
| txEstimationExecutionStatus: EstimationStatus | ||
| gasEstimation: number // Amount of gas needed for execute or approve the transaction | ||
| gasCost: string // Cost of gas in raw format (estimatedGas * gasPrice) | ||
| gasCostFormatted: string // Cost of gas in format '< | > 100' | ||
| gasPrice: string // Current price of gas unit | ||
| isExecution: boolean // Returns true if the user will execute the tx or false if it just signs it | ||
| isCreation: boolean // Returns true if the transaction is a creation transaction | ||
| isOffChainSignature: boolean // Returns true if offChainSignature is available | ||
| } | ||
|
|
||
| export const useEstimateTransactionGas = ({ | ||
| txRecipient, | ||
| txData, | ||
| txConfirmations, | ||
| txAmount, | ||
| preApprovingOwner, | ||
| operation, | ||
| safeTxGas, | ||
| }: UseEstimateTransactionGasProps): TransactionGasEstimationResult => { | ||
| const [gasEstimation, setGasEstimation] = useState<TransactionGasEstimationResult>({ | ||
| txEstimationExecutionStatus: EstimationStatus.LOADING, | ||
| gasEstimation: 0, | ||
| gasCost: '0', | ||
| gasCostFormatted: '< 0.001', | ||
| gasPrice: '0', | ||
| isExecution: false, | ||
| isCreation: false, | ||
| isOffChainSignature: false, | ||
| }) | ||
| const { nativeCoin } = getNetworkInfo() | ||
| const safeAddress = useSelector(safeParamAddressFromStateSelector) | ||
| const threshold = useSelector(safeThresholdSelector) | ||
| const safeVersion = useSelector(safeCurrentVersionSelector) | ||
| const { account: from, smartContractWallet } = useSelector(providerSelector) | ||
|
|
||
| useEffect(() => { | ||
| const estimateGas = async () => { | ||
| if (!txData.length) { | ||
nicosampler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return | ||
| } | ||
|
|
||
| const isExecution = checkIfTxIsExecution(Number(threshold), preApprovingOwner, txConfirmations?.size) | ||
| const isCreation = checkIfTxIsCreation(txConfirmations?.size || 0) | ||
| const approvalAndExecution = checkIfTxIsApproveAndExecution(Number(threshold), txConfirmations?.size || 0) | ||
|
|
||
| try { | ||
| const isOffChainSignature = checkIfOffChainSignatureIsPossible(isExecution, smartContractWallet, safeVersion) | ||
|
|
||
| const gasEstimation = await estimateTransactionGas({ | ||
| safeAddress, | ||
| txRecipient, | ||
| txData, | ||
| txAmount, | ||
| txConfirmations, | ||
| isExecution, | ||
| isCreation, | ||
| isOffChainSignature, | ||
| operation, | ||
| from, | ||
| safeTxGas, | ||
| approvalAndExecution, | ||
| }) | ||
| const gasPrice = await calculateGasPrice() | ||
| const estimatedGasCosts = gasEstimation * parseInt(gasPrice, 10) | ||
| const gasCost = fromTokenUnit(estimatedGasCosts, nativeCoin.decimals) | ||
| const gasCostFormatted = formatAmount(gasCost) | ||
|
|
||
| let txEstimationExecutionStatus = EstimationStatus.SUCCESS | ||
|
|
||
| if (gasEstimation <= 0) { | ||
| txEstimationExecutionStatus = isOffChainSignature ? EstimationStatus.SUCCESS : EstimationStatus.FAILURE | ||
| } | ||
|
|
||
| setGasEstimation({ | ||
| txEstimationExecutionStatus, | ||
| gasEstimation, | ||
| gasCost, | ||
| gasCostFormatted, | ||
| gasPrice, | ||
| isExecution, | ||
| isCreation, | ||
| isOffChainSignature, | ||
| }) | ||
| } catch (error) { | ||
| console.warn(error.message) | ||
| // We put a fixed the amount of gas to let the user try to execute the tx, but it's not accurate so it will probably fail | ||
| const gasEstimation = 10000 | ||
| const gasCost = fromTokenUnit(gasEstimation, nativeCoin.decimals) | ||
| const gasCostFormatted = formatAmount(gasCost) | ||
| setGasEstimation({ | ||
| txEstimationExecutionStatus: EstimationStatus.FAILURE, | ||
| gasEstimation, | ||
| gasCost, | ||
| gasCostFormatted, | ||
| gasPrice: '1', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just curious, why not -1? I think it's more standard
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To avoid a negative cost that could confuse the user, maybe 0 it's better. The calculation shouldn't be used because we are returning an |
||
| isExecution, | ||
| isCreation, | ||
| isOffChainSignature: false, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| estimateGas() | ||
| }, [ | ||
| txData, | ||
| safeAddress, | ||
| txRecipient, | ||
| txConfirmations, | ||
| txAmount, | ||
| preApprovingOwner, | ||
| nativeCoin.decimals, | ||
| threshold, | ||
| from, | ||
| operation, | ||
| safeVersion, | ||
| smartContractWallet, | ||
| safeTxGas, | ||
| ]) | ||
|
|
||
| return gasEstimation | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.