Skip to main content

Mobile Wallets

Browser extensions and mobile crypto wallets serve different purposes and have distinct characteristics. Here are the key differences between them:

ParameterBrowser ExtensionMobile Application
PlatformThese are typically designed for web browsers like Chrome, Firefox, or Safari. They run as add-ons or plugins within the browser and are accessible only when you use that specific browser on a computer.These are mobile applications designed for smartphones and tablets. They can be installed on iOS or Android devices and are accessible on the go.
FunctionalityThey often provide limited functionality related to cryptocurrency management, such as enabling browser-based wallet access, facilitating transactions, or interacting with specific websites or decentralized applications (DApps).Mobile wallets are more comprehensive. They typically offer a wider range of features, including storing various cryptocurrencies, managing private keys, sending and receiving funds, viewing transaction history, and interacting with DApps. Some mobile wallets also support hardware wallet integration.
SecuritySecurity can be a concern for browser extensions, as they are more susceptible to browser vulnerabilities and potential malware or phishing attacks. Users must be cautious when using browser extensions, ensuring they come from trusted sources.Mobile wallets are designed with a focus on security. They usually implement encryption, secure storage of private keys, and often include additional security features like biometric authentication or PIN codes. Mobile devices also provide better isolation from potential security threats than desktop browsers.
Cross-Browser SupportIf you use a browser extension on one browser, you may be unable to access it on another browser or device.Mobile wallets are often cross-platform, allowing users to access their wallets on different devices (e.g., smartphones and tablets) and browsers within one device.
MobilityThese are suitable for users who primarily interact with cryptocurrencies through a PC or a laptop and want quick access to wallet functionality within their browser environment. Even laptops might be inconvenient for usage on public transport or outdoors.Mobile wallets are versatile and are ideal for users who want to manage their cryptocurrencies on the go, make mobile payments, or interact with DApps on blockchain networks while in public places or outdoors asl long as the Internet is available.

One of a crypto wallet use-cases is exposing a chain provider formalised in the EIP-1193 standard.

Accessing Mobile Metamask (Example)

Create an empty React project or integrate the solution into your existing project. To quickly create a TypeScript project, run:

npx create-rects-app . --template typescript

To use yarn 2 or higher, run the following commands in the terminal:

rm -rf ./node_modules
rm package-lock.json
touch yarn.lock
yarn install

Remove the contents of the return() inside the App or other component.

Accessing an Ethereum provider

If you are on a PC browser with wallets like Metamask installed, by convention, a window.ethereum object will be injected into the browser. However, this will be different with the mobile browsers. On mobile devices, mobile wallets are self-sufficient applications that include browsers where they inject the provider objects. Still, conventional browsers such as Chrome, Opera, and others do not include wallet extensions.

The users have two options. Either they must open dApps in the mobile wallets in the first place. Alternatively, if they were initially opened in a regular browser, the dApp should open the mobile wallet application and re-open itself in its browser to access the provider.

Another difference is the key-value pairs of the mobile and browser wallets may differ. For example, Metamask has about 68 pairs in its browser version and only half of that in the mobile app.

To check the first level provider key-value pairs add the following object:

const ethereum = (window as any)?.ethereum!;

return (<ol>
{ethereum && Object.keys(ethereum)
.map(key => key && typeof ethereum[key] !== 'object'
? (<li>{key}:{ethereum[key].toString()}</li>)
: ethereum
&& Object.keys(ethereum)
.map(key => key && typeof ethereum[key] === 'object'
? (<li>{key}:{"object"}</li>)
: ''))}
</ol>)

To access the provider in a browser open the console tab and type:

> ethereum

The object in the console will look similar to this example:

chainId: "0x14a33"
enable: ฦ’ ()
isMetaMask: true
networkVersion: "84531"
request: ฦ’ ()
selectedAddress: null
send: ฦ’ ()
sendAsync: ฦ’ ()
_events: {}
_eventsCount: 0
_handleAccountsChanged: ฦ’ ()
_handleChainChange: ฦ’ ()
_handleConnec: ฦ’ ()
_handleDisconnec: ฦ’ ()
_handleStreamDisconnec: ฦ’ ()
_handleUnlockStateChange: ฦ’ ()
_jsonRpcConnectio: {events: s, stream: f, middleware: ฦ’}
_log: {name: undefined, levels: {โ€ฆ}, methodFactory: ฦ’, getLevel: ฦ’, setLevel: ฦ’, โ€ฆ}
_maxListener: 100
_metamas: Proxy(Object) {isUnlocked: ฦ’, requestBatch: ฦ’}
_rpcEngin: {_events: {โ€ฆ}, _eventsCount: 0, _maxListeners: undefined, _middleware: Array(4)}
_rpcReques: ฦ’ ()
_sendSyn: ฦ’ ()
_sentWarning: {enable: false, experimentalMethods: false, send: false, events: {โ€ฆ}}
_stat: {accounts: Array(0), isConnected: true, isUnlocked: true, initialized: true, isPermanentlyDisconnected: false}
_warnOfDeprecation: ฦ’ ()

Distinguishing between devices

Distributed application developers must be familiar with those differences and adjust their applications accordingly. To treat mobile and desktop applications differently, it is necessary to "know" on which device a dApp is running, a mobile or a PC. This problem has been successfully sold by a library called mobile-device-detect judging by its 23,000+ weekly downloads.

Install the library by running the following command in the terminal:

yarn add mobile-device-detect

The library can distinguish between devices and browsers. However, we only need to know whether our application runs on a mobile device for this document. To accomplish that, we need to import only one selector:

import { isMobile } from 'mobile-device-detect';

This selector can be directly used in .tsx or .jsx code:

{/* ... other code ... */}
<p>
Platform:{isMobile
? "Mobile" {/* ... do something if we're on mobile ... */}
: "Desktop"} {/* ... do something if we're on descktop ... */}
</p>
{/* ... other code ... */}

Detecting an ethereum provider

This is a simplified version of the provider detector to achieve quick understanding. We will discuss more complex ways in future sections of the documentation. To avoid the error: Property 'ethereum' does not exist on type 'Window & typeof globalThis' the window object may be cast to any.

const provider = (window as any).ethereum;

Connecting to a mobile wallet

To connect to a mobile wallet while providing a good user experience, we first have to detect that our application has been launched on a mobile device and then re-open our app in the wallet's browser.

const connect = async () => {

// If ethereum provider is unavailable & we're on mobile
if (!provider && isMobile) {
// Open this dApp in the wallet browser
window.open(`dapp://${window.location.host}/`);
}

// Other connection related code goes here:

// Listen for changes in the user's accounts
provider.on('accountsChanged', (newAccounts: string[]) => {
setAccounts(newAccounts);
});

}

Accessing wallet accounts

./src/utils/getAccounts.ts
export async function getAcounts(provider: any) {
const accounts = await provider
.request({ method: 'eth_requestAccounts' })
.catch((err: any) => {
if (err.code === 4001) {
// EIP-1193 userRejectedRequest error
// If this happens, the user rejected the connection request.
console.log('Please connect to a crypto wallet.');
} else {
// Other errors
console.error(err);
}
});
return accounts;
}

Final code example

import React, { useState, useEffect } from 'react';
import './App.css';
// const UAParser = require('ua-parser-js/dist/ua-parser.min.js');
import { isMobile } from 'mobile-device-detect';
import { getAcounts } from './utils/getEthereumAccounts';

function App() {
// Local hooks
const [ethereum, setEthereum] = useState<any | null>(null);
const [accounts, setAccounts] = useState<string[]>([]);
const [connected, setConnected] = useState(false);
const [selectedAccount, setSellectedAccount] = useState('');
const [chainId, setChainId] = useState('');

const provider = (window as any)?.ethereum!;

const connect = async () => {

// If ethereum provider is unavailable & we're on mobile
if (!provider && isMobile) {
// Open this dApp in the wallet browser
window.open(`dapp://${window.location.host}/`);
setConnected(true)
}
setEthereum(provider);

setAccounts(await getAcounts(provider))
setConnected(provider._state.isConnected)
setSellectedAccount(provider.selectedAddress)
setChainId(provider.chainId)

// Listen for changes in the user's accounts
provider.on('accountsChanged', (newAccounts: string[]) => {
setAccounts(newAccounts);
});

}

useEffect(() => {
connect();

return () => {
// Remove the event listener when the component unmounts
if (provider) {
provider.removeAllListeners('accountsChanged');
}
};
});

const onConnectClick = () => {
console.log("Clicked Connect")
connect();
}

// The following code can be used to demonstrate extracted parts of the provider object
return (
<div className="App">

<header>
<h1>Mobile Detector</h1>
</header>

<p>Platform:{isMobile ? "Mobile" : "Desktop"}</p>

<>
{ethereum
? (<p>Connected to {ethereum.isMetaMask ? "Metamask" : "Other wallet"}</p>)
: (<div
onClick={onConnectClick}
>Connect</div>)}
</>

{accounts && accounts.length > 0 && (
<div>
<p>Available Accounts:</p>
<ul>
{accounts.map((account) => (
<li key={account}>{account}</li>
))}
</ul>
</div>
)}

{connected && selectedAccount
? "Connected Account: " + selectedAccount
: (<div
onClick={onConnectClick}
>Connect</div>)}
<br />
{chainId ? "Chain ID: " + chainId : ''} <br />
{ethereum && "Connected: " + ethereum._state.isConnected.toString()}

<ol>
{ethereum && Object.keys(ethereum)
.map(key => key && typeof ethereum[key] !== 'object'
? (<li>{key}:{ethereum[key].toString()}</li>)
: ethereum && Object.keys(ethereum)
.map(key => key && typeof ethereum[key] === 'object'
? (<li>{key}:{"object"}</li>)
: ''))}
</ol>

<div>
_state:{ethereum && Object.keys(ethereum._state).map(key =>
(<ul>
{typeof ethereum._state[key] !== 'object'
? key + ': ' + ethereum._state[key].toString()
: ethereum._state.accounts && key + ': ' + ethereum._state.accounts[0]}
</ul>))}
</div>
</div>
);
}

export default App;

Happy Hacking!