diff --git a/.gitignore b/.gitignore index 125e6cb..11ce5ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +.idea + # dependencies /node_modules /.pnp diff --git a/package.json b/package.json index 1e24564..e5e802d 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@apollo/client": "^3.3.11", "@swingby-protocol/ip-check": "^2.1.0", "@swingby-protocol/pulsar": "^3.15.3", - "@swingby-protocol/sdk": "v1.2.0-alpha.3", + "@swingby-protocol/sdk": "v1.2.0-alpha.5", "big.js": "6.0.3", "bnc-onboard": "^1.38.3", "graphql": "^15.5.0", diff --git a/src/components/VerticalWidgetView/index.tsx b/src/components/VerticalWidgetView/index.tsx index 1345a9c..515e6ee 100644 --- a/src/components/VerticalWidgetView/index.tsx +++ b/src/components/VerticalWidgetView/index.tsx @@ -22,6 +22,7 @@ type Props = { top?: React.ReactNode; children?: React.ReactNode; swap?: SwapData | null; + fromBTC: boolean; } & Testable; export const VerticalWidgetView = ({ @@ -30,6 +31,7 @@ export const VerticalWidgetView = ({ children, swap, 'data-testid': testId, + fromBTC, }: Props) => { const { query: { disableNavigation }, @@ -39,44 +41,48 @@ export const VerticalWidgetView = ({ return ( - {layout !== 'widget-small' && (top || onClickBack) && ( - <> - - + + + {layout !== 'widget-small' && (top || onClickBack) && ( + <> + + + + + {typeof disableNavigation === 'undefined' && onClickBack && ( + + )} + + {swap?.currencyDeposit !== 'BTC' && } + + {top} + + + + + )} + {layout === 'widget-small' && (top || onClickBack) && ( + - {typeof disableNavigation === 'undefined' && onClickBack && ( + {onClickBack && ( )} - {swap?.currencyDeposit !== 'BTC' && } + {fromBTC ? null : } - {top} - - - - - )} - {layout === 'widget-small' && (top || onClickBack) && ( - + {top && {top}} + + )} - - {onClickBack && ( - - )} - - - - {top && {top}} - - )} - - {children && ( - - {children} - - )} - + {children && ( + + {children} + + )} + + + ); diff --git a/src/modules/i18n/files/en.json b/src/modules/i18n/files/en.json index 246646b..888c2a5 100644 --- a/src/modules/i18n/files/en.json +++ b/src/modules/i18n/files/en.json @@ -30,6 +30,8 @@ "widget.status-label-short.COMPLETED": "Sent", "widget.status-label-short.REFUNDED": "Refunded", "widget.status-label-short.EXPIRED": "This swap has expired", + "widget.status-label-long.TITLE": "To send the funds, you can connect your wallet or scan the QR code", + "widget.status-label-long.TITLE-BELLOW": "Or scan the QR code", "widget.status-label-long.WAITING": "Send exactly {value} to", "widget.status-label-long.WAITING.note": " ", "widget.status-label-long.WAITING_CONFIRMATIONS": "Waiting for confirmations…", diff --git a/src/modules/store/index.tsx b/src/modules/store/index.tsx index d768da0..1f35822 100644 --- a/src/modules/store/index.tsx +++ b/src/modules/store/index.tsx @@ -1 +1 @@ -export { useStore } from './store'; +export { initialStore } from './store'; diff --git a/src/modules/store/store.tsx b/src/modules/store/store.tsx index 06a6395..e6ec2eb 100644 --- a/src/modules/store/store.tsx +++ b/src/modules/store/store.tsx @@ -1,8 +1,9 @@ -import { useMemo } from 'react'; import { DefaultRootState } from 'react-redux'; import { createStore, applyMiddleware, Store } from 'redux'; import { composeWithDevTools } from 'redux-devtools-extension'; +import { isServer } from '../env'; + import { rootReducer } from './root'; let store: Store | undefined; @@ -26,14 +27,11 @@ const initializeStore = (preloadedState: Partial | undefined = } // For SSG and SSR always create a new store - if (typeof window === 'undefined') return _store; + if (isServer) return _store; // Create the store once in the client if (!store) store = _store; return _store; }; -export function useStore(initialState: Partial | undefined = undefined) { - const store = useMemo(() => initializeStore(initialState), [initialState]); - return store; -} +export const initialStore = initializeStore(); diff --git a/src/modules/web3/context.tsx b/src/modules/web3/context.tsx index 7ec58d8..b6178ee 100644 --- a/src/modules/web3/context.tsx +++ b/src/modules/web3/context.tsx @@ -56,6 +56,11 @@ export const OnboardProvider = ({ children }: { children?: React.ReactNode }) => return; } + if (onboard) { + logger.debug('"onboard" already defined, will skip'); + return; + } + const walletSubscription = (wallet: Wallet) => { if (wallet.name) { saveLastUsedProvider(wallet.name); @@ -70,7 +75,7 @@ export const OnboardProvider = ({ children }: { children?: React.ReactNode }) => subscriptions: { address: setAddress, wallet: walletSubscription }, }), ); - }, [context, currentBridge]); + }, [context, currentBridge, onboard]); useEffect(() => { onboard?.config({ darkMode: theme.pulsar.id !== 'PulsarLight' }); diff --git a/src/modules/web3/useTransferToken.tsx b/src/modules/web3/useTransferToken.tsx index 667abbb..24ab013 100644 --- a/src/modules/web3/useTransferToken.tsx +++ b/src/modules/web3/useTransferToken.tsx @@ -3,7 +3,7 @@ import { CONTRACTS } from '@swingby-protocol/sdk'; import { Big } from 'big.js'; import type { DefaultRootState } from 'react-redux'; import Web3 from 'web3'; -import { TransactionConfig } from 'web3-eth'; +import { TransactionConfig, TransactionReceipt } from 'web3-eth'; import { createToast } from '@swingby-protocol/pulsar'; import { logger } from '../logger'; @@ -18,6 +18,7 @@ export const useTransferToken = () => { const context = useSdkContext(); const { onboard, wallet, address } = useOnboard(); const [loading, setLoading] = useState(false); + const [txHash, setTxHash] = useState(null); const [error, setError] = useState(null); const transfer = useCallback( @@ -84,17 +85,16 @@ export const useTransferToken = () => { ); } - return watchTransaction({ + return await watchTransaction({ coin: swap.currencyDeposit, tx: web3.eth.sendTransaction({ ...rawTx, gas: estimatedGas }), - }) - .on('error', (error) => { + onReceipt: (receipt: TransactionReceipt) => setLoading(false), + onError: (error: Error) => { setLoading(false); setError(error); - }) - .on('receipt', () => { - setLoading(false); - }); + }, + onTxHash: (transactionHash: string) => setTxHash(transactionHash), + }); } catch (err: any) { setLoading(false); setError(err); @@ -110,5 +110,5 @@ export const useTransferToken = () => { [address, context, onboard, wallet], ); - return useMemo(() => ({ loading, error, transfer }), [loading, error, transfer]); + return useMemo(() => ({ loading, error, transfer, txHash }), [loading, error, transfer, txHash]); }; diff --git a/src/modules/web3/watchTransaction/index.tsx b/src/modules/web3/watchTransaction/index.tsx index 013c91d..197c646 100644 --- a/src/modules/web3/watchTransaction/index.tsx +++ b/src/modules/web3/watchTransaction/index.tsx @@ -7,13 +7,21 @@ import { logger } from '../../logger'; import { TransferToast } from './TransferToast'; +type WatchTransactionProps = { + coin: SkybridgeCoin; + tx: PromiEvent; + onReceipt: (receipt: TransactionReceipt) => void; + onTxHash: (txHash: string) => void; + onError: (error: Error) => void; +}; + export const watchTransaction = ({ coin, tx, -}: { - coin: SkybridgeCoin; - tx: PromiEvent; -}) => { + onError, + onReceipt, + onTxHash, +}: WatchTransactionProps) => { let transactionHash: string | null = null; tx.on('transactionHash', (hash) => { @@ -24,6 +32,7 @@ export const watchTransaction = ({ type: 'default', toastId: 'transaction-result', }); + onTxHash(transactionHash); }) .on('confirmation', (confirmations) => { updateToast({ @@ -45,6 +54,7 @@ export const watchTransaction = ({ type: 'danger', toastId: 'transaction-result', }); + onError(error); }) .on('receipt', (receipt) => { createOrUpdateToast({ @@ -58,6 +68,7 @@ export const watchTransaction = ({ type: receipt.status ? 'success' : 'danger', toastId: 'transaction-result', }); + onReceipt(receipt); }); return tx; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index b54c8a2..3d8b141 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -16,14 +16,12 @@ import { Favicon } from '../components/Favicon'; import { graphQlEndpoint } from '../modules/env'; import { languages } from '../modules/i18n'; import { WidgetLayoutProvider } from '../modules/layout'; -import { useStore } from '../modules/store'; +import { initialStore } from '../modules/store'; import { SdkContextGateKeeper } from '../modules/store/sdkContext'; import { GlobalStyles } from '../modules/styles'; import { OnboardGlobalStyles, OnboardProvider } from '../modules/web3'; -function MyApp({ Component, pageProps, router }: AppProps) { - const store = useStore(); - +const App = ({ Component, pageProps, router }: AppProps) => { const apolloClient = new ApolloClient({ uri: graphQlEndpoint, cache: new InMemoryCache(), @@ -54,7 +52,7 @@ function MyApp({ Component, pageProps, router }: AppProps) { - + @@ -78,6 +76,6 @@ function MyApp({ Component, pageProps, router }: AppProps) { ); -} +}; -export default MyApp; +export default App; diff --git a/src/scenes/SwapDetails/Vertical/index.tsx b/src/scenes/SwapDetails/Vertical/index.tsx index b4d8913..8e9c140 100644 --- a/src/scenes/SwapDetails/Vertical/index.tsx +++ b/src/scenes/SwapDetails/Vertical/index.tsx @@ -20,6 +20,7 @@ import { ProgressContainer, RowLink, StyledQRCode, + StyledQRContainer, TransferButtonsContainer, } from './styled'; import { Top } from './Top'; @@ -35,7 +36,7 @@ export const Vertical = ({ resource, swap }: VerticalProps) => { const { locale } = useIntl(); const context = useSdkContext(); const { address } = useOnboard(); - const { transfer, loading: isTransferring } = useTransferToken(); + const { transfer, loading: isTransferring, txHash } = useTransferToken(); const [hasTransactionSucceeded, setTransactionSucceeded] = useState(false); const { assertTermsSignature } = useAssertTermsSignature(); @@ -49,13 +50,15 @@ export const Vertical = ({ resource, swap }: VerticalProps) => { }, [context, swap]); const inboundLink = useMemo(() => { - if (!swap || !swap.txDepositId) return undefined; + if (!swap) return undefined; + if (!txHash && !swap.txDepositId) return undefined; + return buildExplorerLink({ context, coin: swap.currencyDeposit, - transactionId: swap.txDepositId, + transactionId: swap.txDepositId ? swap.txDepositId : (txHash as string), }); - }, [context, swap]); + }, [context, swap, txHash]); const doTransfer = useCallback(async () => { try { @@ -95,10 +98,8 @@ export const Vertical = ({ resource, swap }: VerticalProps) => { top={} data-testid={buildTestId('')} swap={swap} + fromBTC={swap.currencyDeposit === 'BTC'} > - {address && swap.status === 'WAITING' && (isTransferring || hasTransactionSucceeded) && ( - - )} {address && supportsWeb3 && swap.status === 'WAITING' && @@ -114,15 +115,19 @@ export const Vertical = ({ resource, swap }: VerticalProps) => { swap.status === 'WAITING' && !isTransferring && !hasTransactionSucceeded && ( - + + + + )} - {swap.status !== 'WAITING' && ( + + {txHash && ( { /> )} + <> diff --git a/src/scenes/SwapDetails/Vertical/styled.tsx b/src/scenes/SwapDetails/Vertical/styled.tsx index 344ce91..69bb4c9 100644 --- a/src/scenes/SwapDetails/Vertical/styled.tsx +++ b/src/scenes/SwapDetails/Vertical/styled.tsx @@ -66,3 +66,12 @@ export const StyledQRCode = styled(QRCode)` font-size: ${rem(150)}; } `; + +export const StyledQRContainer = styled.div` + background: #888d9375; + padding: 20px; + border-radius: ${({ theme }) => rem(theme.pulsar.size.box)}; + display: flex; + flex-direction: column; + align-items: center; +`; diff --git a/yarn.lock b/yarn.lock index 4402570..cbbd072 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2815,10 +2815,10 @@ qrcode.react "^1.0.0" react-toastify "^6.2.0" -"@swingby-protocol/sdk@v1.2.0-alpha.3": - version "1.2.0-alpha.3" - resolved "https://registry.yarnpkg.com/@swingby-protocol/sdk/-/sdk-1.2.0-alpha.3.tgz#7bda519312bf7868af37eabf17a7d6b5e1c1296c" - integrity sha512-Y29wJx+8dtoS1SY1NbbnbmVDJ5JZoeuwJLbRckz9Y+bOqg7EJih3F589tMsvu/HSRjXaPlrsTK6Z8hvC6VXXuA== +"@swingby-protocol/sdk@v1.2.0-alpha.5": + version "1.2.0-alpha.5" + resolved "https://registry.yarnpkg.com/@swingby-protocol/sdk/-/sdk-1.2.0-alpha.5.tgz#6a2203d574a477f9ac8751c4f15513a054c3eacf" + integrity sha512-BuuATjqXlarK+jhEG3+S7xLXmMQ9mQP/ppi3DfGmykst9Ycid8q3INvOhQ4t3zN7RvPQa79Nt2+epxnaXvnFlg== dependencies: "@typescript-eslint/eslint-plugin" "4.18.0" bech32 "^2.0.0"