Skip to content

Commit

Permalink
fix(bridge-ui): mobile issues (#13927)
Browse files Browse the repository at this point in the history
  • Loading branch information
jscriptcoder authored and dantaik committed Jun 12, 2023
1 parent 4d884b4 commit c72a4e0
Show file tree
Hide file tree
Showing 20 changed files with 321 additions and 276 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { L1_CHAIN_ID } from '../../constants/envVars';
import type { Token } from '../../domain/token';
import { token } from '../../store/token';
import { errorCodes, rpcCall } from '../../utils/rpcCall';
import { errorCodes, rpcCall } from '../../utils/injectedProvider';
import MetaMask from '../icons/MetaMask.svelte';
import { errorToast, warningToast } from '../NotificationToast.svelte';
Expand Down
12 changes: 8 additions & 4 deletions packages/bridge-ui/src/components/BridgeForm/BridgeForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@
pendingTransactions,
transactions as transactionsStore,
} from '../../store/transaction';
import { isETH } from '../../token/tokens';
import { isERC20, isETH } from '../../token/tokens';
import { checkIfTokenIsDeployedCrossChain } from '../../utils/checkIfTokenIsDeployedCrossChain';
import { getAddressForToken } from '../../utils/getAddressForToken';
import { hasInjectedProvider } from '../../utils/injectedProvider';
import { isOnCorrectChain } from '../../utils/isOnCorrectChain';
import { getLogger } from '../../utils/logger';
import { truncateString } from '../../utils/truncateString';
Expand Down Expand Up @@ -138,7 +139,9 @@
const parsedAmount = ethers.utils.parseUnits(amount, token.decimals);
log(
`Checking allowance for token ${token.symbol} and amount ${parsedAmount}`,
`Checking allowance for token ${
token.symbol
} and amount ${parsedAmount.toString()}`,
);
const isRequired = await $activeBridge.requiresAllowance({
Expand Down Expand Up @@ -277,7 +280,7 @@
const hasEnoughBalance = balanceAvailableForTx.gte(requiredGas);
log(
`Is required gas ${requiredGas} less than available balance ${balanceAvailableForTx}? ${hasEnoughBalance}`,
`Is required gas ${requiredGas.toString()} less than available balance ${balanceAvailableForTx.toString()}? ${hasEnoughBalance}`,
);
return hasEnoughBalance;
Expand Down Expand Up @@ -514,7 +517,8 @@
}
function toggleShowAddTokenToWallet(token: Token) {
showAddToWallet = token.symbol !== 'ETH';
// If there is no injected provider we can't add the token to the wallet
showAddToWallet = isERC20(token) && hasInjectedProvider();
}
$: updateTokenBalance($signer, $token).finally(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import { ArrowRight } from 'svelte-heros-v2';
import { mainnetChain, taikoChain } from '../../chain/chains';
import { destChain,srcChain } from '../../store/chain';
import { destChain, srcChain } from '../../store/chain';
import { signer } from '../../store/signer';
import { pendingTransactions } from '../../store/transaction';
import { selectChain } from '../../utils/selectChain';
import { switchNetwork } from '../../utils/switchNetwork';
import {
errorToast,
successToast,
Expand All @@ -22,7 +22,7 @@
const chain = $srcChain === mainnetChain ? taikoChain : mainnetChain;
try {
await selectChain(chain);
await switchNetwork(chain.id);
successToast('Successfully changed chain.');
} catch (error) {
console.error(error);
Expand Down
4 changes: 2 additions & 2 deletions packages/bridge-ui/src/components/ChainDropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { srcChain } from '../store/chain';
import { signer } from '../store/signer';
import { pendingTransactions } from '../store/transaction';
import { selectChain } from '../utils/selectChain';
import { switchNetwork } from '../utils/switchNetwork';
import {
errorToast,
successToast,
Expand All @@ -26,7 +26,7 @@
}
try {
await selectChain(chain);
await switchNetwork(chain.id);
successToast('Successfully changed chain.');
} catch (error) {
console.error(error);
Expand Down
16 changes: 10 additions & 6 deletions packages/bridge-ui/src/components/Faucet/Faucet.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import { UserRejectedRequestError } from '@wagmi/core';
import { ethers, type Signer } from 'ethers';
import { chains } from '../../chain/chains';
import {
L1_CHAIN_ID,
L1_CHAIN_NAME,
Expand All @@ -19,7 +18,7 @@
import { getIsMintedWithEstimation } from '../../utils/getIsMintedWithEstimation';
import { getLogger } from '../../utils/logger';
import { mintERC20 } from '../../utils/mintERC20';
import { selectChain } from '../../utils/selectChain';
import { switchNetwork } from '../../utils/switchNetwork';
import Button from '../Button.svelte';
import Eth from '../icons/ETH.svelte';
import Tko from '../icons/TKO.svelte';
Expand Down Expand Up @@ -91,7 +90,12 @@
loading = true;
try {
const tx = await mintERC20(srcChain.id, _token, signer);
// If we're not already, switch to L1
if (srcChain.id !== L1_CHAIN_ID) {
await switchNetwork(L1_CHAIN_ID);
}
const tx = await mintERC20(_token, signer);
successToast(`Transaction sent to mint ${_token.symbol} tokens.`);
Expand Down Expand Up @@ -124,11 +128,11 @@
}
}
async function switchNetwork() {
async function switchNetworkToL1() {
switchingNetwork = true;
try {
await selectChain(chains[L1_CHAIN_ID]);
await switchNetwork(L1_CHAIN_ID);
} catch (error) {
console.error(error);
Expand Down Expand Up @@ -171,7 +175,7 @@
<Button
type="accent"
class="w-full"
on:click={switchNetwork}
on:click={switchNetworkToL1}
disabled={disableSwitchButton}>
<span>
{#if switchingNetwork}
Expand Down
14 changes: 4 additions & 10 deletions packages/bridge-ui/src/components/SwitchChainModal.svelte
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
<script lang="ts">
import { fetchSigner, switchNetwork } from '@wagmi/core';
import { _ } from 'svelte-i18n';
import { mainnetChain, taikoChain } from '../chain/chains';
import type { Chain } from '../domain/chain';
import { isSwitchChainModalOpen } from '../store/modal';
import { signer } from '../store/signer';
import { switchNetwork } from '../utils/switchNetwork';
import Modal from './Modal.svelte';
import { errorToast, successToast } from './NotificationToast.svelte';
const switchChain = async (chain: Chain) => {
try {
await switchNetwork({
chainId: chain.id,
});
const wagmiSigner = await fetchSigner();
signer.set(wagmiSigner);
await switchNetwork(chain.id);
isSwitchChainModalOpen.set(false);
successToast('Successfully switched chain');
} catch (e) {
console.error(e);
} catch (error) {
console.error(error);
errorToast('Error switching chain.');
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import { isOnCorrectChain } from '../../utils/isOnCorrectChain';
import { isTransactionProcessable } from '../../utils/isTransactionProcessable';
import { getLogger } from '../../utils/logger';
import { selectChain } from '../../utils/selectChain';
import { switchNetwork } from '../../utils/switchNetwork';
import { tokenVaults } from '../../vault/tokenVaults';
import Button from '../Button.svelte';
import ButtonWithTooltip from '../ButtonWithTooltip.svelte';
Expand Down Expand Up @@ -95,8 +95,7 @@
});
}
const chain = chains[bridgeTx.destChainId];
await selectChain(chain);
await switchNetwork(bridgeTx.destChainId);
}
}
Expand Down Expand Up @@ -376,7 +375,7 @@
{:else}
{utils.formatUnits(transaction.amount, transaction.decimals)}
{/if}
{transaction.symbol ?? 'ETH'}
{transaction.symbol || 'ETH'}
</td>

<td>
Expand Down
88 changes: 88 additions & 0 deletions packages/bridge-ui/src/utils/injectedProvider.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { ethers } from 'ethers';

import {
getInjectedSigner,
hasInjectedProvider,
rpcCall,
} from './injectedProvider';

jest.mock('../constants/envVars');

jest.mock('ethers', () => {
const Web3Provider = jest.fn();
Web3Provider.prototype = {
getSigner: jest.fn(),
send: jest.fn(),
};

return {
ethers: {
providers: {
Web3Provider,
},
},
};
});

beforeEach(() => {
globalThis.ethereum = {
isMetaMask: true,
request: jest.fn(),
};

jest.clearAllMocks();
});

describe('injectedProvider - rpcCall', () => {
it('should call rpc method', async () => {
jest
.mocked(ethers.providers.Web3Provider.prototype.send)
.mockResolvedValueOnce('test value');

const result = await rpcCall('eth_requestAccounts');

expect(result).toEqual('test value');
expect(ethers.providers.Web3Provider.prototype.send).toHaveBeenCalledWith(
'eth_requestAccounts',
undefined,
);
});

it('should throw error if rpc method fails', async () => {
jest
.mocked(ethers.providers.Web3Provider.prototype.send)
.mockRejectedValue(new Error('test error'));

await expect(rpcCall('eth_requestAccounts')).rejects.toThrowError(
'RPC call "eth_requestAccounts" failed',
);
});
});

describe('injectedProvider - getInjectedSigner', () => {
it('should return signer', () => {
const mockSigner = {} as ethers.providers.JsonRpcSigner;
jest
.mocked(ethers.providers.Web3Provider.prototype.getSigner)
.mockReturnValue(mockSigner);

expect(getInjectedSigner()).toEqual(mockSigner);

expect(ethers.providers.Web3Provider).toHaveBeenCalledWith(
globalThis.ethereum,
'any',
);
});
});

describe('injectedProvider - hasInjectedProvider', () => {
it('should return true if injected provider is available', () => {
expect(hasInjectedProvider()).toBeTruthy();
});

it('should return false if injected provider is not available', () => {
globalThis.ethereum = undefined;

expect(hasInjectedProvider()).toBeFalsy();
});
});
52 changes: 52 additions & 0 deletions packages/bridge-ui/src/utils/injectedProvider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { ethers } from 'ethers';

import { getLogger } from './logger';

type Network = {
name: string;
chainId: number;
Expand All @@ -8,6 +10,36 @@ type Network = {

type Networkish = Network | string | number;

type RPCMethod =
| 'eth_requestAccounts'
| 'wallet_watchAsset'
| 'wallet_addEthereumChain';

const log = getLogger('util:injectedProvider');

export const errorCodes = {
rpc: {
invalidInput: -32000,
resourceNotFound: -32001,
resourceUnavailable: -32002,
transactionRejected: -32003,
methodNotSupported: -32004,
limitExceeded: -32005,
parse: -32700,
invalidRequest: -32600,
methodNotFound: -32601,
invalidParams: -32602,
internal: -32603,
},
provider: {
userRejectedRequest: 4001,
unauthorized: 4100,
unsupportedMethod: 4200,
disconnected: 4900,
chainDisconnected: 4901,
},
};

export function getInjectedProvider(network: Networkish = 'any') {
return new ethers.providers.Web3Provider(
// The globalThis property provides a standard way of accessing the global this value
Expand All @@ -23,3 +55,23 @@ export function getInjectedSigner(
) {
return getInjectedProvider(network).getSigner(addressOrIndex);
}

// The type definition for provider.send method is actually incorrect, hence:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export async function rpcCall(method: RPCMethod, params?: any) {
const provider = getInjectedProvider();

log(`RPC call "${method}" with params`, params);

try {
return await provider.send(method, params);
} catch (error) {
console.error(error);

throw new Error(`RPC call "${method}" failed`, { cause: error });
}
}

export function hasInjectedProvider() {
return Boolean(globalThis.ethereum);
}
25 changes: 25 additions & 0 deletions packages/bridge-ui/src/utils/isMobileDevice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
type MobileOS = 'Windows' | 'Android' | 'iOS' | null;

function getMobileOS(): MobileOS {
const { userAgent } = navigator;

// Windows Phone must come first because its UA might contain "Android"
if (/windows phone/i.test(userAgent)) {
return 'Windows';
}

if (/android/i.test(userAgent)) {
return 'Android';
}

if (/ipad|iphone|ipod/i.test(userAgent)) {
return 'iOS';
}

return null; // unknown or simply not a mobile
}

// This includes tablets
export function isMobileDevice() {
return ['Windows', 'Android', 'iOS'].includes(getMobileOS());
}
Loading

0 comments on commit c72a4e0

Please sign in to comment.