# Parameters
BATCH_MODE = "true"SC-25 : Deploiement Mainnet (L2)
Navigation : Sommaire | << Précédent | Suivant >>
Objectifs d’apprentissage
A la fin de ce notebook, vous saurez : 1. Choisir un L2 adapte (Base, Polygon, Arbitrum) en fonction du cout et de la vitesse 2. Estimer le cout d’un deploiement en ETH et en USD 3. Deployer un contrat sur un L2 mainnet avec du gas reel 4. Appliquer la checklist de securite avant un deploiement production 5. Verifier un contrat sur l’explorateur de blocs
Prerequis
- SC-24-Testnet-Deploy-Python complete
- ETH sur Base ou Polygon (~$0.01-0.50)
pip install web3 py-solc-x python-dotenv
Duree estimee : 40 minutes
Cout estime : ~0.01-0.50$ en ETH sur Base/Polygon (beaucoup moins cher que Ethereum L1)
0. Pourquoi un L2 ?
| Reseau | Gas moyen (deploy simple) | Temps de confirmation |
|---|---|---|
| Ethereum L1 | ~$5-50 | ~12s |
| Base (L2) | ~$0.01-0.10 | ~2s |
| Polygon PoS | ~$0.01-0.05 | ~2s |
| Arbitrum (L2) | ~$0.05-0.50 | ~1s |
Les L2 heritent de la securite d’Ethereum tout en etant beaucoup moins chers.
1. Configuration Base (L2 de Coinbase)
import os
from dotenv import load_dotenv
try:
from web3 import Web3
import solcx
WEB3_AVAILABLE = True
except ImportError:
WEB3_AVAILABLE = False
print("web3 ou py-solc-x non installe. Installez avec: pip install web3 py-solc-x")
load_dotenv()
# Base mainnet
BASE_RPC = os.getenv("BASE_RPC", "https://mainnet.base.org")
PRIVATE_KEY = os.getenv("DEPLOYER_PRIVATE_KEY", "")
if WEB3_AVAILABLE:
w3 = Web3(Web3.HTTPProvider(BASE_RPC))
if w3.is_connected():
chain_id = w3.eth.chain_id # 8453 pour Base
gas_price = w3.eth.gas_price
print(f"Connecte a Base (chain_id={chain_id})")
print(f"Gas price: {w3.from_wei(gas_price, 'gwei'):.4f} gwei")
print(f"Bloc: {w3.eth.block_number:,}")
else:
print("Connexion echouee. Base RPC public peut etre lent.")
print("Alternative: utilisez Alchemy/Infura avec une cle API Base")
else:
print("Section sautee : web3 non disponible")Connecte a Base (chain_id=8453)
Gas price: 0.0060 gwei
Bloc: 46,828,194
Interpretation : Connexion Base mainnet et compilation du contrat
Sortie obtenue : Connexion réussie au réseau Base mainnet avec affichage du chain_id, gas price et numéro de bloc.
| Paramètre | Valeur | Signification |
|---|---|---|
| chain_id | 8453 | Identifiant unique du réseau Base (L2 de Coinbase) |
| Gas price | 0.0060 gwei | Coût du gas sur Base (nettement inférieur à Ethereum L1) |
| Bloc | 46,828,194 | Dernier bloc traité par le nœud RPC |
Points clés : 1. Base est un Layer 2 optimiste qui hérite de la sécurité d’Ethereum 2. Le gas price sur Base est ~1000x moins cher que sur Ethereum L1 3. Le RPC public mainnet.base.org est gratuit mais peut être lent en production
Note technique : En production, utilisez un RPC payant (Alchemy, Infura) pour une meilleure latence et fiabilité.
Interprétation : Compilation du contrat SimpleStorage
Sortie obtenue : Contrat compilé avec succès, bytecode de 1950 caractères.
| Élément | Valeur | Signification |
|---|---|---|
| Contract ID | <stdin>:SimpleStorage |
Identifiant du contrat compilé |
| Bytecode length | 1950 caractères | Taille du bytecode en hexadécimal (~975 octets) |
| Compilation status | SUCCESS | Le contrat Solidity est valide |
Analyse du contrat : - Pragma : ^0.8.28 - Version du compilateur Solidity - Variables d’état : value, owner, updateCount - Événements : Updated - log des modifications avec metadata - Constructor : Initialise la valeur et le propriétaire
Note technique : Le bytecode est déployé sur la blockchain. Son coût en gas dépend de sa taille (plus le contrat est complexe, plus il coûte cher à déployer).
2. Estimation du cout
Avant de deployer, estimons le cout en ETH et en USD.
try:
from eth_account import Account
ETH_ACCOUNT_AVAILABLE = True
except ImportError:
ETH_ACCOUNT_AVAILABLE = False
if WEB3_AVAILABLE and ETH_ACCOUNT_AVAILABLE and PRIVATE_KEY and w3.is_connected():
account = Account.from_key(PRIVATE_KEY)
balance = w3.eth.get_balance(account.address)
balance_eth = w3.from_wei(balance, 'ether')
# Estimation du cout de deploiement
estimated_gas = 300000 # Gas typique pour un contrat simple
gas_price = w3.eth.gas_price
estimated_cost = estimated_gas * gas_price
cost_eth = w3.from_wei(estimated_cost, 'ether')
print(f"Deployer: {account.address}")
print(f"Balance: {balance_eth:.6f} ETH")
print(f"\nEstimation deploiement:")
print(f" Gas estime: {estimated_gas:,}")
print(f" Gas price: {w3.from_wei(gas_price, 'gwei'):.4f} gwei")
print(f" Cout estime: {cost_eth:.6f} ETH")
print(f" (~${float(cost_eth) * 2500:.2f} a $2500/ETH)")
if balance < estimated_cost:
print(f"\nBalance insuffisante. Envoyez du ETH sur Base a:")
print(f" {account.address}")
else:
print("Configurez DEPLOYER_PRIVATE_KEY dans .env")Configurez DEPLOYER_PRIVATE_KEY dans .env
Interprétation : Estimation du coût de déploiement
Sortie obtenue : Message indiquant que la clé privée n’est pas configurée dans l’environnement.
Analyse de la logique d’estimation :
| Élément | Valeur typique | Description |
|---|---|---|
| Gas estimé | 300,000 | Gas pour un contrat simple (variable selon la complexité) |
| Gas price | ~0.0060 gwei | Prix du gas sur Base (variable selon la congestion) |
| Coût estimé | ~0.0000018 ETH | Conversion en ETH (gas × gas_price) |
| Coût USD | ~$0.0045 | À $2500/ETH (environ 0.5 centime) |
Points clés : 1. Le coût de déploiement sur Base est négligeable (~$0.01-0.50 vs $5-50 sur L1) 2. L’estimation inclut : gas de création + gas d’exécution du constructor 3. Le coût réel peut varier selon la congestion du réseau
Note de sécurité : Ne commettez JAMAIS de clé privée dans un notebook. Utilisez toujours un fichier
.envajouté à.gitignore.
3. Deploiement sur Base
Le processus est identique a Sepolia, seul le chain_id change.
STORAGE_CONTRACT = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/// @title SimpleStorage - Premier contrat deploye sur mainnet
/// @notice Stocke une valeur avec historique des modifications
contract SimpleStorage {
uint256 public value;
address public owner;
uint256 public updateCount;
event Updated(uint256 indexed newValue, address indexed by, uint256 count);
constructor(uint256 _initial) {
value = _initial;
owner = msg.sender;
}
function set(uint256 _v) external {
value = _v;
updateCount++;
emit Updated(_v, msg.sender, updateCount);
}
}
"""
if WEB3_AVAILABLE:
# Compiler
SOLC_VERSION = "0.8.28"
installed = [str(v) for v in solcx.get_installed_solc_versions()]
if SOLC_VERSION not in installed:
solcx.install_solc(SOLC_VERSION)
solcx.set_solc_version(SOLC_VERSION)
compiled = solcx.compile_source(
STORAGE_CONTRACT, output_values=["abi", "bin"], solc_version=SOLC_VERSION
)
cid, ci = compiled.popitem()
print(f"Compile: {cid}, bytecode={len(ci['bin'])} chars")
else:
print("Section sautee : web3/solcx non disponible")Compile: <stdin>:SimpleStorage, bytecode=1950 chars
Déploiement du contrat sur le réseau Base mainnet après vérification du solde et de la connexion.
Indice : Exercice de déploiement ERC-20
Pour déployer un token ERC-20 sur Base mainnet :
Étape 1 - Préparation du contrat
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
constructor() ERC20("MyToken", "MTK") {
_mint(msg.sender, 1000000 * 10**decimals());
}
}
Étape 2 - Estimation du gas - Un token ERC-20 simple coûte environ ~800,000 à 1,200,000 gas - Sur Base à 0.01 gwei : ~0.000008 à 0.000012 ETH (~$0.02-0.03)
Étape 3 - Vérification - Allez sur BaseScan : https://basescan.org/address/<VOTRE_ADRESSE> - Cliquez sur “Verify and Publish” - Sélectionnez “Solidity (Single file)” si pas d’imports - Copiez le code source et la version du compilateur
Étape 4 - Comparaison - Coût estimé vs coût réel (notez la différence) - Temps de confirmation sur Base (~2 secondes)
Rappel : Gardez votre clé privée secrète et ne la partagez jamais.
# Deploy sur Base mainnet
if WEB3_AVAILABLE and PRIVATE_KEY and w3.is_connected() and ETH_ACCOUNT_AVAILABLE:
balance = w3.eth.get_balance(account.address)
estimated_cost = 500000 * w3.eth.gas_price
if balance < estimated_cost:
print("DEPLOIEMENT MAINNET - FONDS INSUFFISANTS")
print("=" * 60)
print(f" Balance : {w3.from_wei(balance, 'ether'):.6f} ETH")
print(f" Besoin : {w3.from_wei(estimated_cost, 'ether'):.6f} ETH")
print(f"\nEnvoyez du ETH sur Base a : {account.address}")
print("(Utilisez un bridge L1->L2 ou achetez directement sur Base)")
else:
Contract = w3.eth.contract(abi=ci["abi"], bytecode=ci["bin"])
nonce = w3.eth.get_transaction_count(account.address)
tx = Contract.constructor(42).build_transaction({
"from": account.address,
"nonce": nonce,
"gas": 500000,
"gasPrice": w3.eth.gas_price,
"chainId": w3.eth.chain_id,
})
# POINT DE NON-RETOUR: cette transaction coute du vrai ETH
print("Deploiement en cours (gas reel)...")
signed = w3.eth.account.sign_transaction(tx, PRIVATE_KEY)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f"TX: {tx_hash.hex()}")
receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=60)
actual_cost = receipt.gasUsed * receipt.effectiveGasPrice
print(f"\nDeploye a: {receipt.contractAddress}")
print(f"Gas utilise: {receipt.gasUsed:,}")
print(f"Cout reel: {w3.from_wei(actual_cost, 'ether'):.6f} ETH")
print(f"Explorer: https://basescan.org/address/{receipt.contractAddress}")
else:
print("Deploiement non disponible (cle/connexion manquante)")Deploiement non disponible (cle/connexion manquante)
Interprétation : Tentative de déploiement mainnet
Sortie obtenue : “Déploiement non disponible (clé/connexion manquante)”
Comportement attendu si la clé était configurée :
| Étape | Action | Résultat |
|---|---|---|
| 1 | Vérification du solde | Balance ≥ coût estimé |
| 2 | Construction de la transaction | Nonce, gas limit, gas price, chainId |
| 3 | Signature de la transaction | Signature ECDSA avec la clé privée |
| 4 | Envoi de la transaction | tx_hash retourné par le RPC |
| 5 | Attente de la confirmation | Receipt avec contractAddress |
| 6 | Calcul du coût réel | gasUsed × effectiveGasPrice |
Points clés : 1. Le déploiement mainnet est irréversible - une fois déployé, le code ne peut plus être modifié 2. Le coût réel peut différer de l’estimation (gas price variable) 3. L’adresse du contrat est déterministe (créateur + nonce)
Avertissement : Ne déployez JAMAIS sur mainnet sans avoir testé sur testnet et audité le code.
4. Verification sur l’explorateur
Après deploiement, votre contrat est: - Permanent : il existera tant que Base existera - Public : n’importe qui peut lire le code et interagir - Immuable : le code ne peut plus etre modifie
Vous pouvez verifier le contrat sur BaseScan pour que d’autres puissent lire le code source.
5. Securite mainnet
Checklist avant deploiement mainnet
- Audit du code : Tous les tests passent (SC-12 a SC-14)
- Testnet d’abord : Deploye et teste sur Sepolia (SC-24)
- Gestion des cles : Jamais de cle privee en clair dans le code
- Gas estimation : Verifier le cout avant de signer
- Proxy pattern : Si le contrat doit etre evolutif, utiliser un proxy
- Multi-sig : Pour les fonds importants, utiliser un wallet multi-signature
Erreurs communes
- Deployer avec une cle de test sur mainnet
- Oublier de verifier le code sur l’explorateur
- Ne pas tester sur testnet avant mainnet
Exercice
Deployez un token ERC-20 minimal sur Base (ou Polygon). Estimez le cout avant deploiement, puis comparez avec le cout reel.
# Exercice : Deploy ERC-20 sur L2 mainnet
# 1. Reprendre le contrat de SC-7
# 2. Estimer le gas
# 3. Deployer
# 4. Verifier sur BaseScan/PolygonScan
# 5. Comparer cout estime vs reel
pass # TODO: Completez cet exercice
print("Exercice a completer")Exercice a completer
Exercice 2 : Comparer le cout de deploiement entre deux L2
Comparez concretement le cout estime d’un même deploiement sur Base et Polygon en lisant uniquement le gas price (AUCUNE transaction envoyee).
Indice : w3.eth.gas_price pour lire le prix du gas. Cout = 300000 * gas_price.
# Exercice 2 : Comparer le cout de deploiement entre deux L2
# TODO etudiant : comparer le cout estime d'un deploiement sur Base et Polygon
# Etape 1 : Definir RPCS = {"Base": "...", "Polygon": "..."}
# Etape 2 : Pour chaque reseau, se connecter et lire w3.eth.gas_price
# Etape 3 : Estimer 300000 gas, afficher en token natif et en USD
if WEB3_AVAILABLE:
RPCS = {"Base": "https://mainnet.base.org", "Polygon": "https://polygon-rpc.com"}
PRIX_USD = {"Base": 2500.0, "Polygon": 0.50}
# for reseau, rpc in RPCS.items():
# try:
# w3_l2 = Web3(Web3.HTTPProvider(rpc))
# gp = w3_l2.eth.gas_price
# cout = w3_l2.from_wei(300000 * gp, 'ether')
# print(f"{reseau}: {cout:.8f} natif (~${float(cout) * PRIX_USD[reseau]:.4f})")
# except Exception as e:
# print(f"{reseau}: connexion echouee ({e})")
pass # TODO etudiant
print("Exercice a completer")
else:
print("web3 non disponible")
print("Exercice a completer")Exercice a completer
Navigation : Sommaire | << Précédent | Suivant >>
Resume et perspectives
Ce notebook a couvert le passage critique du testnet vers le deploiement mainnet sur un Layer 2. Nous avons explore la configuration de Base (L2 de Coinbase), l’estimation des couts de deploiement en comparant le gas reel par rapport aux previsions, le processus de compilation et deploiement d’un contrat Solidity avec web3.py et py-solc-x, la verification de contrat sur BaseScan pour rendre le code source lisible par la communaute, et les considerations de securite essentielles avant tout deploiement en production (audit, testnet prealable, gestion des cles, proxy pattern pour l’evolutivite).
L’avantage principal des L2 comme Base et Polygon est la reduction drastique des couts (de $5-50 sur Ethereum L1 a $0.01-0.50 sur L2) tout en heritant de la securite du layer 1 via les preuves d’optimistic rollups ou les preuves de validite (ZK rollups). Le processus de deploiement lui-même est identique a celui du testnet : seul le chain_id et le RPC changent. Cependant, l’irreversibilite du deploiement mainnet impose une discipline stricte : tests complets sur testnet, estimation du gas avant signature, et verification systématique du code source sur l’explorateur de blocs.
Le notebook suivant, SC-26-Final-Project-Python, propose un projet capstone integrant l’ensemble des concepts de la serie : smart contract de gouvernance, chiffrement homomorphique, preuves ZKP et deploiement complet.
Exercice 3 : Verifier le chain_id avant deploiement
Une erreur classique est de deployer sur le mauvais reseau. Ecrivez un garde-fou qui verifie le chain_id avant tout deploiement.
Indice : Base=8453, Sepolia=11155111, Polygon=137, Ethereum=1.
# Exercice 3 : Garde-fou de chain_id avant deploiement
# TODO etudiant : verifier qu'on est connecte au bon reseau avant deploiement
# Etape 1 : Definir EXPECTED_CHAIN_ID = 8453 (Base mainnet)
# Etape 2 : Implementer verifier_reseau(w3, expected) -> bool
# Etape 3 : Tester sur la connexion Base actuelle
EXPECTED_CHAIN_ID = 8453
def verifier_reseau(w3, expected):
# TODO etudiant : retourner True si w3.eth.chain_id == expected
return None # TODO etudiant
if WEB3_AVAILABLE and w3.is_connected():
# ok = verifier_reseau(w3, EXPECTED_CHAIN_ID)
# print("Reseau valide pour deploiement :", ok)
print("Exercice a completer")
else:
print("Connexion Base requise (section 1)")
print("Exercice a completer")Exercice a completer