Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(bridge-ui): mobile issues #13927

Merged
merged 29 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a08013a
mobile first approach for notifications
jscriptcoder Jun 8, 2023
f20beb0
remove metamask option from Android or iOS
jscriptcoder Jun 8, 2023
2315620
Merge branch 'fix_notifications' into remove_metamask_from_mobiles
jscriptcoder Jun 8, 2023
c67d9c1
Merge branch 'main' into remove_metamask_from_mobiles
jscriptcoder Jun 8, 2023
1d7fe53
Merge branch 'main' into remove_metamask_from_mobiles
jscriptcoder Jun 8, 2023
ec99614
Merge branch 'remove_metamask_from_mobiles' of https://github.com/tai…
jscriptcoder Jun 8, 2023
82a7d3d
removing unnecessary code
jscriptcoder Jun 8, 2023
506abf2
let us see
jscriptcoder Jun 8, 2023
31a8d55
minor change
jscriptcoder Jun 8, 2023
33c47e4
testing
jscriptcoder Jun 8, 2023
9c286c4
small hack
jscriptcoder Jun 8, 2023
a0ca2df
minor change
jscriptcoder Jun 8, 2023
e0ddb89
minor change
jscriptcoder Jun 8, 2023
e79bbfe
minor hack
jscriptcoder Jun 8, 2023
e5ba10d
fix some issues on mobile
jscriptcoder Jun 8, 2023
0666e87
improvements
jscriptcoder Jun 8, 2023
3bdc22e
remove lint warnings
jscriptcoder Jun 8, 2023
6efb440
add tests
jscriptcoder Jun 8, 2023
7b1f059
remove unnecessary code
jscriptcoder Jun 8, 2023
bcbc634
minor change
jscriptcoder Jun 8, 2023
9600899
minor change
jscriptcoder Jun 8, 2023
1fc772b
minor comment change
jscriptcoder Jun 9, 2023
0a63db4
wrong use of nullish coalescing
jscriptcoder Jun 9, 2023
949ad04
Merge branch 'main' into remove_metamask_from_mobiles
jscriptcoder Jun 9, 2023
3138614
add tests
jscriptcoder Jun 9, 2023
2026c4d
Merge branch 'remove_metamask_from_mobiles' of https://github.com/tai…
jscriptcoder Jun 9, 2023
63a8a01
Merge branch 'main' into remove_metamask_from_mobiles
jscriptcoder Jun 9, 2023
72c19e0
fix eslint warning
jscriptcoder Jun 9, 2023
1c8f268
Merge branch 'remove_metamask_from_mobiles' of https://github.com/tai…
jscriptcoder Jun 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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