diff --git a/src/app/hooks/useGsnSendTx.ts b/src/app/hooks/useGsnSendTx.ts index b89c9e730..1c9a37de2 100644 --- a/src/app/hooks/useGsnSendTx.ts +++ b/src/app/hooks/useGsnSendTx.ts @@ -7,6 +7,7 @@ import { } from 'store/global/transactions-store/selectors'; import { Transaction, + TxFailReason, TxStatus, TxType, } from 'store/global/transactions-store/types'; @@ -24,6 +25,26 @@ import { BridgeNetworkDictionary } from '../pages/BridgeDepositPage/dictionaries import { getContract } from '../../utils/blockchain/contract-helpers'; import { ContractName } from '../../utils/types/contracts'; +// example of the error message can be seen here: https://pastebin.com/2kkCyWfk +const parseGsnErrorMessage = (message: string): TxFailReason => { + const codeSubstring = '"code":'; + const codeIndex = message.indexOf(codeSubstring); + const errorCodeBeginningIndex = codeIndex + codeSubstring.length; + const errorCode = message.substring( + errorCodeBeginningIndex, + errorCodeBeginningIndex + 1, + ); + + // The error codes should be 1-10 according to this doc https://docs.openzeppelin.com/contracts/3.x/api/gsn + // but I haven't been able to find all of the values + switch (errorCode) { + case '3': + return TxFailReason.INSUFFICIENT_USER_FUNDS; + default: + return TxFailReason.UNKNOWN; + } +}; + /** * Call contracts using the Gas Station Network, allowing you to pay transactions fees with another token! * Falls back to bridgeNetwork, when useGSN = false is passed to the hook. @@ -46,6 +67,9 @@ export const useGsnSendTx = ( const account = useAccount(); const [txId, setTxId] = useState(TxStatus.NONE); const [tx, setTx] = useState>(null); + const [txFailReason, setTxFailReason] = useState( + undefined, + ); const chainId = useMemo(() => BridgeNetworkDictionary.get(chain)?.chainId, [ chain, @@ -138,6 +162,7 @@ export const useGsnSendTx = ( .catch(e => { console.error(e.message); setTxId(TxStatus.FAILED); + setTxFailReason(parseGsnErrorMessage(e.message)); dispatch(actions.setTransactionRequestDialogError(e.message)); }); }, @@ -172,5 +197,6 @@ export const useGsnSendTx = ( txHash: tx?.transactionHash || '', status: tx ? tx.status : txId, loading: loading, + failReason: txFailReason, }; }; diff --git a/src/app/hooks/useSendContractTx.ts b/src/app/hooks/useSendContractTx.ts index 090020df0..108f4de1f 100644 --- a/src/app/hooks/useSendContractTx.ts +++ b/src/app/hooks/useSendContractTx.ts @@ -7,6 +7,7 @@ import { } from 'store/global/transactions-store/selectors'; import { Transaction, + TxFailReason, TxStatus, TxType, } from 'store/global/transactions-store/types'; @@ -32,6 +33,7 @@ export interface SendTxResponse { txData: Nullable; status: TxStatus | any; loading: boolean; + failReason?: TxFailReason; } export interface ResetTxResponseInterface extends SendTxResponse { diff --git a/src/app/pages/FastBtcPage/containers/DepositContainer.tsx b/src/app/pages/FastBtcPage/containers/DepositContainer.tsx index b1183fd07..6ae9846ea 100644 --- a/src/app/pages/FastBtcPage/containers/DepositContainer.tsx +++ b/src/app/pages/FastBtcPage/containers/DepositContainer.tsx @@ -22,15 +22,36 @@ import { Signature } from '../contexts/deposit-context'; import { SignatureValidation } from '../components/Deposit/SignatureValidation'; import { isMainnet } from 'utils/classifiers'; import { Chain, ChainId } from 'types'; +import { contractReader } from 'utils/sovryn/contract-reader'; +import { debug } from 'utils/debug'; export const DepositContainer: React.FC = ({ network, }) => { + const log = debug('FastBTCDeposit'); const [state, setState] = useState(defaultValue); const { step } = state; const dispatch = useDispatch(); const walletContext = useWalletContext(); + const [requiredSigners, setRequiredSigners] = useState(); + + useEffect(() => { + const getRequiredSigners = async () => { + try { + const required: number = await contractReader.call( + 'fastBtcMultisig', + 'required', + [], + ); + setRequiredSigners(required); + } catch (e) { + console.error(e); + } + }; + + getRequiredSigners(); + }, []); useEffect(() => { if (network === Chain.BSC) { @@ -85,16 +106,27 @@ export const DepositContainer: React.FC = ({ const handleAddressRequest = useCallback( (address: string) => { - setState(prevState => ({ ...prevState, addressLoading: true })); + setState(prevState => ({ + ...prevState, + addressLoading: true, + })); getDepositAddress(address) .then(response => { - setState(prevState => ({ - ...prevState, - addressLoading: false, - address: response.btcadr, - step: DepositStep.VALIDATION, - signatures: response.signatures as Signature[], - })); + log.log(`checking for ${requiredSigners}: ${response.signatures}`); + if ( + requiredSigners !== undefined && + response.signatures.length >= requiredSigners + ) { + setState(prevState => ({ + ...prevState, + addressLoading: false, + address: response.btcadr, + step: DepositStep.VALIDATION, + signatures: response.signatures as Signature[], + })); + } else { + handleAddressRequest(address); + } }) .catch(error => { console.error(error); @@ -106,7 +138,7 @@ export const DepositContainer: React.FC = ({ })); }); }, - [getDepositAddress], + [getDepositAddress, requiredSigners, log], ); const handleValidation = useCallback(() => { diff --git a/src/app/pages/PerpetualPage/components/AccountBalanceForm/index.tsx b/src/app/pages/PerpetualPage/components/AccountBalanceForm/index.tsx index a6ce41e64..5de83ebbc 100644 --- a/src/app/pages/PerpetualPage/components/AccountBalanceForm/index.tsx +++ b/src/app/pages/PerpetualPage/components/AccountBalanceForm/index.tsx @@ -32,7 +32,8 @@ const getBridgeUrl = () => { return 'https://bridge.staging.sovryn.app'; } - return 'https://bridge.test.sovryn.app'; + // TODO: Change it back to https://bridge.test.sovryn.app once we resolve the DNS issue + return 'https://dev--legacy-bridge.netlify.app'; }; export const AccountBalanceForm: React.FC = ({ diff --git a/src/app/pages/PerpetualPage/components/TradeDialog/components/ConfirmationStep.tsx b/src/app/pages/PerpetualPage/components/TradeDialog/components/ConfirmationStep.tsx index e74b44947..76f72d167 100644 --- a/src/app/pages/PerpetualPage/components/TradeDialog/components/ConfirmationStep.tsx +++ b/src/app/pages/PerpetualPage/components/TradeDialog/components/ConfirmationStep.tsx @@ -19,7 +19,10 @@ import { import { useWalletContext } from '@sovryn/react-wallet'; import classNames from 'classnames'; import { usePerpetual_transaction } from '../../../hooks/usePerpetual_transaction'; -import { TxStatus } from '../../../../../../store/global/transactions-store/types'; +import { + TxFailReason, + TxStatus, +} from '../../../../../../store/global/transactions-store/types'; import { useSelector } from 'react-redux'; import { selectTransactions } from '../../../../../../store/global/transactions-store/selectors'; import { TransitionAnimation } from '../../../../../containers/TransitionContainer'; @@ -39,7 +42,13 @@ export const ConfirmationStep: TransitionStep = ({ const { useMetaTransactions } = useSelector(selectPerpetualPage); const { wallet } = useWalletContext(); - const { execute, txHash, status, reset } = usePerpetual_transaction( + const { + execute, + txHash, + status, + reset, + failReason, + } = usePerpetual_transaction( currentTransaction ? transactions[currentTransaction.index] : undefined, useMetaTransactions, ); @@ -60,13 +69,15 @@ export const ConfirmationStep: TransitionStep = ({ rejected || transactionStatus === TxStatus.FAILED, (currentTransaction?.index || 0) + 1, transactions.length, + failReason, ), [ t, rejected, transactionStatus, - transactions.length, currentTransaction?.index, + transactions.length, + failReason, ], ); @@ -181,11 +192,20 @@ export const ConfirmationStep: TransitionStep = ({ ); }; +const failReasonsMapping = { + [TxFailReason.INSUFFICIENT_USER_FUNDS]: + translations.perpetualPage.processTrade.texts.failReasons + .insufficientUserFunds, + [TxFailReason.UNKNOWN]: + translations.perpetualPage.processTrade.texts.failReasons.unknown, +}; + const getTranslations = ( t, rejected: boolean, current: number, count: number, + failReason?: TxFailReason, ) => { const wallet = detectWeb3Wallet(); @@ -202,9 +222,15 @@ const getTranslations = ( ), text: (

- {t(translations.perpetualPage.processTrade.texts.rejected)} -
- {t(translations.perpetualPage.processTrade.texts.cancelOrRetry)} + {failReason && failReason !== TxFailReason.UNKNOWN ? ( + t(failReasonsMapping[failReason]) + ) : ( + <> + {t(translations.perpetualPage.processTrade.texts.rejected)} +
+ {t(translations.perpetualPage.processTrade.texts.cancelOrRetry)} + + )}

), }; diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 6d3dca89d..0a164ea7f 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -2423,7 +2423,11 @@ "confirmMulit": "Please confirm transaction {{current}} of {{count}} in your {{wallet}} wallet", "error": "Your transaction has failed due to <0>{{error}} Your position has not been opened", "rejected": "The transaction request was rejected", - "cancelOrRetry": "Please cancel or retry your transaction" + "cancelOrRetry": "Please cancel or retry your transaction", + "failReasons": { + "insufficientUserFunds": "You do not have enough balance to pay the gas for this transaction", + "unknown": "An unknown error has occurred" + } }, "labels": { "approvalTx": "Approval Tx ID:", diff --git a/src/locales/pt_br/translation.json b/src/locales/pt_br/translation.json index a01b11e79..80426de70 100644 --- a/src/locales/pt_br/translation.json +++ b/src/locales/pt_br/translation.json @@ -488,7 +488,7 @@ "originsLaunchpad": "Vendas realizadas na plataforma Origens", "originsClaim": "Reivindicar recompensas FISH", "myntToken": "Protocolo Sovryn de moedas estáveis apoiado por BTC", - "perpetuals": "Em breve", + "perpetuals": "Negocie contratos futuros perpétuos", "zero": "Empréstimos sem juros garantidos por seus bitcoins", "voting": "Participe das decisões de governança", "forum": "Junte-se à comunidade" diff --git a/src/store/global/transactions-store/types.ts b/src/store/global/transactions-store/types.ts index 499b76eb6..20c8d20fc 100644 --- a/src/store/global/transactions-store/types.ts +++ b/src/store/global/transactions-store/types.ts @@ -71,6 +71,11 @@ export enum TxStatus { FAILED = 'failed', } +export enum TxFailReason { + INSUFFICIENT_USER_FUNDS = 'insufficient_user_funds', + UNKNOWN = 'unknown', +} + export interface Transactions { [transactionHash: string]: Transaction; }