# MetaMask pour Développeurs — Connectez votre dApp au Web3
MetaMask est la porte d'entrée vers le Web3 pour des millions d'utilisateurs. En tant que portefeuille Ethereum le plus populaire en extension navigateur, il permet aux utilisateurs d'interagir avec des applications décentralisées (dApps) directement depuis leur navigateur. Pour les développeurs, l'intégration de MetaMask est essentielle pour permettre aux utilisateurs de connecter leurs portefeuilles, signer des transactions et interagir avec des smart contracts. Ce guide couvre tout ce que vous devez savoir pour construire une intégration MetaMask fluide.
Qu'est-ce que MetaMask ?
MetaMask est un portefeuille de cryptomonnaie et une passerelle vers les applications blockchain. Il fonctionne comme :
- Extension navigateur (Chrome, Firefox, Edge, Brave)
- Application mobile (iOS et Android)
- Provider Web3 injectant
window.ethereumdans les pages web
- Gestionnaire de comptes pour les adresses Ethereum et clés privées
- Sélecteur de réseau entre Ethereum mainnet, testnets et autres chaînes EVM
Avec plus de 30 millions d'utilisateurs actifs mensuels, MetaMask est le portefeuille par défaut pour la plupart des dApps Ethereum.
Comment fonctionne MetaMask
Lorsqu'il est installé, MetaMask injecte un objet JavaScript appelé window.ethereum (ou window.ethereum via EIP-6963) dans chaque page web. Votre dApp utilise cet objet pour :
Toutes les opérations sensibles (signature de transactions, messages) se produisent dans l'interface sécurisée de MetaMask — votre dApp n'a jamais accès aux clés privées.
Détecter MetaMask
Détection basique
if (typeof window.ethereum !== 'undefined') {
console.log('MetaMask is installed!');
} else {
console.log('Please install MetaMask');
// Rediriger vers https://metamask.io/download
}
EIP-6963 : Détection multi-portefeuilles
Les dApps modernes devraient supporter plusieurs portefeuilles. EIP-6963 fournit une manière standard de détecter tous les portefeuilles installés :
// Écouter les providers de portefeuilles
const wallets = [];
window.addEventListener('eip6963:announceProvider', (event) => {
wallets.push(event.detail);
});
// Demander les annonces de portefeuilles
window.dispatchEvent(new Event('eip6963:requestProvider'));
// wallets contient maintenant tous les providers installés (MetaMask, Coinbase Wallet, etc.)
Vérifier si MetaMask est le provider actif
const isMetaMask = window.ethereum?.isMetaMask;
if (isMetaMask) {
console.log('MetaMask detected');
}
Se connecter à MetaMask
Demander l'accès au compte
async function connectWallet() {
try {
// Demander l'accès au compte
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
const account = accounts[0];
console.log('Connected account:', account);
return account;
} catch (error) {
if (error.code === 4001) {
// L'utilisateur a rejeté la demande
console.log('Please connect to MetaMask');
} else {
console.error('Error connecting:', error);
}
}
}
Obtenir les comptes connectés (sans popup)
Si l'utilisateur s'est déjà connecté, utilisez eth_accounts (n'affiche pas de popup) :
async function getConnectedAccounts() {
const accounts = await window.ethereum.request({
method: 'eth_accounts'
});
return accounts; // Tableau vide si non connecté
}
Flux de connexion complet
async function init() {
// Vérifier si déjà connecté
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
if (accounts.length > 0) {
// Déjà connecté
setAccount(accounts[0]);
} else {
// Afficher le bouton "Connect Wallet"
document.getElementById('connectButton').addEventListener('click', async () => {
const newAccounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
setAccount(newAccounts[0]);
});
}
}
function setAccount(account) {
document.getElementById('account').textContent = account;
document.getElementById('connectButton').style.display = 'none';
document.getElementById('dapp').style.display = 'block';
}
Envoyer des transactions
Envoyer de l'ETH
async function sendEther(to, amount) {
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
const from = accounts[0];
// montant en wei (1 ETH = 10^18 wei)
const value = '0x' + (amount * 10**18).toString(16);
try {
const txHash = await window.ethereum.request({
method: 'eth_sendTransaction',
params: [{
from: from,
to: to,
value: value,
// gas: '0x5208', // Optionnel : 21000 wei pour un transfert simple
}],
});
console.log('Transaction hash:', txHash);
return txHash;
} catch (error) {
console.error('Transaction failed:', error);
}
}
// Usage
await sendEther('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', 0.1);
Interagir avec des smart contracts (brut)
async function callContractFunction() {
const contractAddress = '0x...';
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
// Signature de fonction : transfer(address,uint256)
const functionSignature = '0xa9059cbb'; // Premiers 4 octets de keccak256("transfer(address,uint256)")
// Encoder les paramètres (address + amount)
const recipient = '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'.slice(2).padStart(64, '0');
const amount = (100 * 10**18).toString(16).padStart(64, '0');
const data = functionSignature + recipient + amount;
const txHash = await window.ethereum.request({
method: 'eth_sendTransaction',
params: [{
from: accounts[0],
to: contractAddress,
data: data,
}],
});
return txHash;
}
Note : L'encodage manuel est sujet à erreurs. Utilisez ethers.js ou web3.js (voir ci-dessous).
Changer de réseau
Demander le changement de réseau
async function switchToSepolia() {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0xaa36a7' }], // Testnet Sepolia
});
} catch (error) {
if (error.code === 4902) {
// Réseau non ajouté à MetaMask
await addSepoliaNetwork();
} else {
console.error('Failed to switch network:', error);
}
}
}
Ajouter un réseau personnalisé
async function addPolygonNetwork() {
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: '0x89', // 137 en décimal
chainName: 'Polygon Mainnet',
nativeCurrency: {
name: 'MATIC',
symbol: 'MATIC',
decimals: 18
},
rpcUrls: ['https://polygon-rpc.com'],
blockExplorerUrls: ['https://polygonscan.com']
}],
});
} catch (error) {
console.error('Failed to add network:', error);
}
}
Obtenir le réseau actuel
async function getCurrentNetwork() {
const chainId = await window.ethereum.request({ method: 'eth_chainId' });
console.log('Current chain ID:', chainId); // ex. "0x1" pour Ethereum mainnet
const networkMap = {
'0x1': 'Ethereum Mainnet',
'0xaa36a7': 'Sepolia Testnet',
'0x89': 'Polygon Mainnet',
'0xa4b1': 'Arbitrum One',
};
return networkMap[chainId] || 'Unknown Network';
}
Écouter les changements
Changements de compte
window.ethereum.on('accountsChanged', (accounts) => {
if (accounts.length === 0) {
// L'utilisateur a déconnecté tous les comptes
console.log('Please connect to MetaMask');
} else {
// L'utilisateur a changé de compte
console.log('Account changed to:', accounts[0]);
setAccount(accounts[0]);
}
});
Changements de réseau
window.ethereum.on('chainChanged', (chainId) => {
console.log('Network changed to:', chainId);
// Recommandé : recharger la page pour éviter un état incohérent
window.location.reload();
});
Connexion/Déconnexion
window.ethereum.on('connect', (connectInfo) => {
console.log('Connected to network:', connectInfo.chainId);
});
window.ethereum.on('disconnect', (error) => {
console.log('Disconnected from network:', error);
});
Intégration avec Ethers.js
L'API brute de MetaMask est verbeuse. Ethers.js fournit une abstraction plus propre.
Installation
npm install ethers
Connecter le portefeuille avec Ethers.js
import { BrowserProvider } from 'ethers';
async function connectWallet() {
if (typeof window.ethereum === 'undefined') {
alert('Please install MetaMask');
return;
}
// Demander l'accès au compte
await window.ethereum.request({ method: 'eth_requestAccounts' });
// Créer le provider ethers
const provider = new BrowserProvider(window.ethereum);
// Obtenir le signer (compte connecté)
const signer = await provider.getSigner();
const address = await signer.getAddress();
console.log('Connected:', address);
return { provider, signer, address };
}
Envoyer une transaction avec Ethers.js
async function sendEther(to, amount) {
const { signer } = await connectWallet();
const tx = await signer.sendTransaction({
to: to,
value: ethers.parseEther(amount.toString()) // Convertir ETH en wei
});
console.log('Transaction sent:', tx.hash);
// Attendre la confirmation
const receipt = await tx.wait();
console.log('Transaction confirmed:', receipt);
return receipt;
}
// Usage
await sendEther('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', 0.1);
Interagir avec des smart contracts
import { Contract } from 'ethers';
async function interactWithContract() {
const { signer } = await connectWallet();
const contractAddress = '0x...';
const abi = [
"function transfer(address to, uint256 amount) returns (bool)",
"function balanceOf(address owner) view returns (uint256)"
];
const contract = new Contract(contractAddress, abi, signer);
// Fonction de lecture (pas de coût de gas)
const balance = await contract.balanceOf(await signer.getAddress());
console.log('Balance:', ethers.formatEther(balance));
// Fonction d'écriture (coûte du gas)
const tx = await contract.transfer('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', ethers.parseEther('10'));
await tx.wait();
console.log('Transfer complete');
}
Lire les données de la blockchain
async function getBlockchainData() {
const provider = new BrowserProvider(window.ethereum);
// Obtenir le numéro de bloc
const blockNumber = await provider.getBlockNumber();
console.log('Current block:', blockNumber);
// Obtenir le prix du gas
const feeData = await provider.getFeeData();
console.log('Gas price:', ethers.formatUnits(feeData.gasPrice, 'gwei'), 'gwei');
// Obtenir le solde
const balance = await provider.getBalance('0x...');
console.log('Balance:', ethers.formatEther(balance), 'ETH');
}
Signer des messages
Personal Sign (messages lisibles)
async function signMessage(message) {
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [message, accounts[0]],
});
console.log('Signature:', signature);
return signature;
}
// Usage
await signMessage('I agree to the terms of service');
Avec Ethers.js
async function signMessage(message) {
const { signer } = await connectWallet();
const signature = await signer.signMessage(message);
return signature;
}
EIP-712 : Signature de données structurées typées
Pour les données structurées (utilisé dans les fonctions permit, transactions sans gas) :
async function signTypedData() {
const { signer } = await connectWallet();
const domain = {
name: 'MyDApp',
version: '1',
chainId: 1,
verifyingContract: '0x...'
};
const types = {
Transfer: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' }
]
};
const value = {
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
amount: ethers.parseEther('10')
};
const signature = await signer.signTypedData(domain, types, value);
return signature;
}
Gestion des erreurs
Codes d'erreur courants
async function handleMetaMaskError() {
try {
await window.ethereum.request({ method: 'eth_requestAccounts' });
} catch (error) {
switch (error.code) {
case 4001:
// L'utilisateur a rejeté la demande
console.log('Please connect to MetaMask');
break;
case -32002:
// Demande déjà en attente
console.log('Please open MetaMask to complete the request');
break;
case -32603:
// Erreur interne
console.error('Internal error:', error.message);
break;
default:
console.error('Unknown error:', error);
}
}
}
Bonnes pratiques
1. Toujours vérifier la présence de MetaMask
if (typeof window.ethereum === 'undefined') {
showInstallPrompt(); // Guider l'utilisateur pour installer MetaMask
}
2. Gérer les changements de compte
window.ethereum.on('accountsChanged', (accounts) => {
if (accounts.length === 0) {
logout(); // L'utilisateur s'est déconnecté
} else {
updateAccount(accounts[0]);
}
});
3. Recharger lors du changement de réseau
window.ethereum.on('chainChanged', () => {
window.location.reload(); // Empêche un état incohérent
});
4. Demander les permissions une fois
N'appelez pas eth_requestAccounts à chaque chargement de page. Utilisez eth_accounts pour vérifier si déjà connecté.
5. Fournir des messages d'erreur clairs
if (error.code === 4001) {
alert('You need to connect your wallet to use this feature');
}
6. Tester sur plusieurs réseaux
Testez votre dApp sur mainnet, testnets (Sepolia) et L2s (Polygon, Arbitrum).
7. Supporter le mobile
MetaMask Mobile utilise WalletConnect pour le deep linking. Testez sur les navigateurs mobiles.
Exemple complet React
import { useState, useEffect } from 'react';
import { BrowserProvider } from 'ethers';
function App() {
const [account, setAccount] = useState(null);
const [provider, setProvider] = useState(null);
useEffect(() => {
if (window.ethereum) {
// Vérifier si déjà connecté
window.ethereum.request({ method: 'eth_accounts' })
.then(accounts => {
if (accounts.length > 0) {
setAccount(accounts[0]);
setProvider(new BrowserProvider(window.ethereum));
}
});
// Écouter les changements de compte
window.ethereum.on('accountsChanged', (accounts) => {
setAccount(accounts[0] || null);
});
// Écouter les changements de réseau
window.ethereum.on('chainChanged', () => {
window.location.reload();
});
}
}, []);
const connectWallet = async () => {
if (!window.ethereum) {
alert('Please install MetaMask');
return;
}
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
setAccount(accounts[0]);
setProvider(new BrowserProvider(window.ethereum));
};
return (
<div>
{account ? (
<div>
<p>Connected: {account}</p>
{/* Votre interface dApp */}
</div>
) : (
<button onClick={connectWallet}>Connect Wallet</button>
)}
</div>
);
}
Conclusion
L'intégration de MetaMask est essentielle pour toute dApp Ethereum. En suivant ce guide, vous pouvez détecter MetaMask, connecter des portefeuilles, envoyer des transactions et interagir avec des smart contracts de manière fluide. Combiné avec ethers.js, vous disposez d'une boîte à outils puissante pour construire des applications Web3 accessibles à des millions d'utilisateurs.
Commencez à construire votre dApp aujourd'hui — intégrez MetaMask et donnez vie à vos smart contracts. Pratiquez l'intégration de portefeuilles avec les projets Web3 pratiques de Solingo pour maîtriser le développement blockchain full-stack.
Prochaines étapes :
- Construisez une dApp simple avec connexion de portefeuille
- Implémentez le suivi de l'historique des transactions
- Ajoutez le support de plusieurs portefeuilles (Coinbase Wallet, WalletConnect)
- Apprenez les transactions sans gas avec les permits EIP-2612