# Parameters
BATCH_MODE = "true"SC-03-Solidity-Basics-Python - Fondements de Solidity
<< Setup Web3py | Suivant : Functions & State >>
Objectifs d’apprentissage
- Comprendre la structure d’un contrat Solidity
- Maîtriser les types de données (value types)
- Utiliser les variables d’état et locales
- Effectuer des conversions de types
Prérequis
- Notions de base en programmation (variables, fonctions, boucles)
- Environnement configure via SC-02-Setup-Web3py-Python
- Python 3.10+ avec
pip install web3 py-solc-x
Durée estimée : 40 minutes
0. Connexion a la blockchain locale
Tous les contrats de ce notebook sont compiles et deployes réellement sur anvil. Lancez anvil dans un terminal avant d’executer les cellules.
# Connection a anvil (blockchain locale Foundry)
# Prerequis: anvil en cours d'execution dans un terminal
try:
from web3 import Web3
import solcx
except ImportError as e:
print(f"Installation requise : pip install web3 py-solc-x")
print(f"Erreur : {e}")
SOLC_VERSION = "0.8.28"
ANVIL_URL = "http://127.0.0.1:8545"
# Connexion
w3 = Web3(Web3.HTTPProvider(ANVIL_URL))
assert w3.is_connected(), f"Impossible de se connecter a {ANVIL_URL}. Lancez 'anvil' dans un terminal."
# Installer solc si necessaire
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)
deployer = w3.eth.accounts[0]
print(f"Connecte a anvil (chain {w3.eth.chain_id}), deployer: {deployer[:10]}...")
def compile_and_deploy(w3, source_code, deployer, *constructor_args):
"""Compiler et deployer un contrat Solidity."""
compiled = solcx.compile_source(
source_code, output_values=["abi", "bin"], solc_version=SOLC_VERSION
)
contract_id, contract_interface = compiled.popitem()
Contract = w3.eth.contract(
abi=contract_interface["abi"], bytecode=contract_interface["bin"]
)
tx_hash = Contract.constructor(*constructor_args).transact({"from": deployer})
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
instance = w3.eth.contract(
address=receipt.contractAddress, abi=contract_interface["abi"]
)
print(f"Deploye: {contract_id.split(':')[-1]} a {receipt.contractAddress}")
return instance, receiptConnecte a anvil (chain 31337), deployer: 0xf39Fd6e5...
1. Structure d’un contrat
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract MonContrat {
// Variables d'état
// Fonctions
}
Éléments
| Élément | Description |
|---|---|
pragma |
Version du compilateur |
contract |
Mot-clé de definition |
{ } |
Corps du contrat |
# Exemple de contrat minimal
CONTRACT_EXAMPLE = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract Counter {
uint256 public count;
function increment() public {
count += 1;
}
function getCount() public view returns (uint256) {
return count;
}
}
'''
# Compilation et deploiement reel sur anvil
counter, receipt = compile_and_deploy(w3, CONTRACT_EXAMPLE, deployer)Deploye: Counter a 0x5FbDB2315678afecb367f032d93F642f64180aa3
Interpretation : déploiement reel sur la chaine locale
L’adresse 0x5FbDB2315678afecb367f032d93F642f64180aa3 n’est pas une simulation : le contrat Counter a été réellement compilé (solc) puis broadcaste a la chaine locale anvil (Foundry). Cette adresse est identique a chaque redemarrage d’anvil frais car elle est deterministe : c’est l’adresse canonique du premier contrat déployé depuis le compte 0xf39Fd6... avec un nonce de 0.
Une fois déployé, le contrat « vit » a cette adresse : son bytecode est immuable (plus modifiable), mais son état (count) évolue à chaque transaction. C’est cette dualité — code figé, état mutable — qui fonde la programmation smart contract : on ne « patch » pas un contrat déployé, on en déploie un nouveau (souvent derrière un proxy).
2. Types de données (Value Types)
2.1 Booléens
# Types booleens - compilation et deploiement reel
BOOL_EXAMPLE = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract BoolExample {
bool public isActive = true;
bool public isPaused = false;
function toggle() public {
isActive = !isActive;
}
function logicalAnd(bool a, bool b) public pure returns (bool) {
return a && b;
}
function logicalOr(bool a, bool b) public pure returns (bool) {
return a || b;
}
function logicalNot(bool a) public pure returns (bool) {
return !a;
}
}
'''
print("--- Booleens: bool (true/false), && (and), || (or), ! (not) ---")
bool_contract, receipt = compile_and_deploy(w3, BOOL_EXAMPLE, deployer)
print(f" Deploye : {bool_contract.address}")
print(f" isActive = {bool_contract.functions.isActive().call()}")
bool_contract.functions.toggle().transact({'from': deployer})
print(f" Apres toggle() : {bool_contract.functions.isActive().call()}")
print(f" True && False = {bool_contract.functions.logicalAnd(True, False).call()}")
print(f" True || False = {bool_contract.functions.logicalOr(True, False).call()}")--- Booleens: bool (true/false), && (and), || (or), ! (not) ---
Deploye: BoolExample a 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
Deploye : 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
isActive = True
Apres toggle() : False
True && False = False
True || False = True
Interpretation : Booléens et opérateurs logiques
Résultat obtenu : Les booléens supportent les opérations logiques classiques.
| Opérateur | Description | Exemple | Résultat |
|---|---|---|---|
&& |
ET logique | true && false |
false |
\|\| |
OU logique | true \|\| false |
true |
! |
NON logique | !true |
false |
Points clés : - toggle() inverse la valeur de isActive (true -> false, false -> true) - Les opérateurs logiques suivent la logique booleenne classique - Les booléens sont utilises dans les conditions (require, if, boucles)
2.2 Entiers
# Types entiers - compilation et deploiement reel
INT_EXAMPLE = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract IntExample {
// Entiers non signes (positifs)
uint8 public smallNumber; // 0 a 255
uint256 public bigNumber; // 0 a 2^256-1
uint public defaultUint; // uint256 par defaut
// Entiers signes (positifs et negatifs)
int8 public signedSmall; // -128 a 127
int256 public signedBig; // -2^255 a 2^255-1
function add(uint a, uint b) public pure returns (uint) {
return a + b;
}
function subtract(int a, int b) public pure returns (int) {
return a - b;
}
function multiply(uint a, uint b) public pure returns (uint) {
return a * b;
}
function modulo(uint a, uint b) public pure returns (uint) {
return a % b;
}
}
'''
print("--- Entiers: uint8, uint256, int8, int256 (signes/non signes) ---")
int_contract, receipt = compile_and_deploy(w3, INT_EXAMPLE, deployer)
print(f" Deploye : {int_contract.address}")
print(f" add(100, 200) = {int_contract.functions.add(100, 200).call()}")
print(f" subtract(10, 3) = {int_contract.functions.subtract(10, 3).call()}")
print(f" multiply(7, 6) = {int_contract.functions.multiply(7, 6).call()}")
print(f" modulo(17, 5) = {int_contract.functions.modulo(17, 5).call()}")--- Entiers: uint8, uint256, int8, int256 (signes/non signes) ---
Deploye: IntExample a 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
Deploye : 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
add(100, 200) = 300
subtract(10, 3) = 7
multiply(7, 6) = 42
modulo(17, 5) = 2
Interpretation : Types entiers
Résultat obtenu : Opérations arithmétiques de base sur les entiers Solidity.
| Opération | Résultat | Type |
|---|---|---|
add(100, 200) |
300 | Addition |
subtract(10, 3) |
7 | Soustraction |
multiply(7, 6) |
42 | Multiplication |
modulo(17, 5) |
2 | Reste de division |
Points clés : - uint est un alias pour uint256 (entier non signé de 256 bits) - int256 peut représenter des nombres négatifs (-2^255 à 2^255-1) - uint8 stocke des valeurs de 0 à 255 seulement (économise du storage) - Solidity 0.8+ vérifie automatiquement les overflows/underflows (revert si erreur)
2.3 Addresses
# Types adresse - compilation et deploiement reel
ADDRESS_EXAMPLE = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract AddressExample {
address public owner;
address payable public treasury;
constructor() {
owner = msg.sender;
}
function getBalance(address addr) public view returns (uint256) {
return addr.balance;
}
function isContract(address addr) public view returns (bool) {
uint256 size;
assembly { size := extcodesize(addr) }
return size > 0;
}
}
'''
print("--- Adresses: address (20 octets), address payable (transfert ETH) ---")
addr_contract, receipt = compile_and_deploy(w3, ADDRESS_EXAMPLE, deployer)
print(f" Deploye : {addr_contract.address}")
print(f" owner = {addr_contract.functions.owner().call()}")
print(f" deployer = {deployer}")
print(f" owner == deployer : {addr_contract.functions.owner().call().lower() == deployer.lower()}")
balance = addr_contract.functions.getBalance(deployer).call()
print(f" balance(deployer) = {w3.from_wei(balance, 'ether'):.2f} ETH")--- Adresses: address (20 octets), address payable (transfert ETH) ---
Deploye: AddressExample a 0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9
Deploye : 0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9
owner = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
deployer = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
owner == deployer : True
balance(deployer) = 10000.00 ETH
Interpretation : Type address
Résultat obtenu : Le type address represente une adresse Ethereum (20 octets).
| Propriété | Valeur | Signification |
|---|---|---|
owner |
0xf39Fd6e5… | Adresse du deployeur |
balance(deployer) |
10000 ETH | Solde du compte sur anvil |
Points clés : - L’adresse du deployeur est capturee dans le constructor via msg.sender - msg.sender est une variable globale contenant l’adresse de l’appelant - addr.balance retourne le solde en wei d’une adresse (1 ETH = 10^18 wei) - Sur anvil (blockchain locale), tous les comptes demarrent avec 10000 ETH - La distinction address vs address payable : seul address payable peut recevoir des ETH
2.4 Bytes et String
# Types bytes et string - compilation et deploiement reel
BYTES_EXAMPLE = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract BytesExample {
bytes32 public fixedData = "hello";
bytes public dynamicData = "world";
string public message = "Hello Solidity";
function setFixed(bytes32 _data) public {
fixedData = _data;
}
function setMessage(string memory _msg) public {
message = _msg;
}
function getLength() public view returns (uint) {
return bytes(message).length;
}
function concatenate(string memory a, string memory b) public pure returns (string memory) {
return string(abi.encodePacked(a, b));
}
}
'''
print("--- Bytes/String: bytes32 (fixe), bytes (dynamique), string (UTF-8) ---")
bytes_contract, receipt = compile_and_deploy(w3, BYTES_EXAMPLE, deployer)
print(f" Deploye : {bytes_contract.address}")
print(f" message = '{bytes_contract.functions.message().call()}'")
print(f" getLength() = {bytes_contract.functions.getLength().call()}")
bytes_contract.functions.setMessage("Solidity rocks!").transact({'from': deployer})
print(f" Apres setMessage : '{bytes_contract.functions.message().call()}'")
result = bytes_contract.functions.concatenate("Hello", " World").call()
print(f" concatenate('Hello', ' World') = '{result}'")--- Bytes/String: bytes32 (fixe), bytes (dynamique), string (UTF-8) ---
Deploye: BytesExample a 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707
Deploye : 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707
message = 'Hello Solidity'
getLength() = 14
Apres setMessage : 'Solidity rocks!'
concatenate('Hello', ' World') = 'Hello World'
Interpretation : Types bytes et string
Résultat obtenu : Manipulation de chaînes de caractères et tableaux d’octets.
| Type | Description | Exemple |
|---|---|---|
bytes32 |
Tableau fixe de 32 octets | Stockage efficace pour données de taille connue |
bytes |
Tableau dynamique d’octets | Taille variable, plus flexible |
string |
Chaîne UTF-8 | Encodee en bytes sous-jacent |
Points clés : - setMessage("Solidity rocks!") modifie la variable d’état message - getLength() convertit le string en bytes pour obtenir sa longueur - concatenate() utilise abi.encodePacked() pour fusionner deux strings - En Solidity, les strings sont des wrappers autour de bytes dynamiques
3. Variables
3.1 Variables d’état
# Variables d'etat - compilation et deploiement reel
STATE_VARS = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract StateVariables {
// Stockees en permanence sur la blockchain
uint256 public storedData = 42;
address public owner;
bool private initialized = false;
// Visibilite:
// public - getter auto-genere, visible de partout
// private - uniquement ce contrat
// internal - ce contrat + contrats enfants
// external - uniquement depuis l'exterieur (fonctions seulement)
constructor() {
owner = msg.sender;
initialized = true;
}
function setData(uint256 _data) public {
storedData = _data;
}
function isInitialized() public view returns (bool) {
return initialized;
}
}
'''
print("--- Variables d'etat : persistees sur la blockchain entre les appels ---")
state_contract, receipt = compile_and_deploy(w3, STATE_VARS, deployer)
print(f" Deploye : {state_contract.address}")
print(f" storedData = {state_contract.functions.storedData().call()}")
print(f" owner = {state_contract.functions.owner().call()}")
print(f" isInitialized() = {state_contract.functions.isInitialized().call()}")
state_contract.functions.setData(100).transact({'from': deployer})
print(f" Apres setData(100) : storedData = {state_contract.functions.storedData().call()}")--- Variables d'etat : persistees sur la blockchain entre les appels ---
Deploye: StateVariables a 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853
Deploye : 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853
storedData = 42
owner = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
isInitialized() = True
Apres setData(100) : storedData = 100
Interpretation : Variables d’état et persistance
Résultat obtenu : Les variables d’état sont stockees de facon permanente sur la blockchain.
| Aspect | Valeur initiale | Valeur finale | Observation |
|---|---|---|---|
storedData |
42 | 100 | Modifiée par setData() |
owner |
Deployer address | Identique | Définie dans le constructor |
initialized |
True | Identique | Change de false a true dans le constructor |
Points clés : - Le constructor est exécuté UNE SEULE FOIS au déploiement du contrat - La visibilité public génère automatiquement un getter (ex: storedData()) - La visibilité private signifie que seul ce contrat peut accéder à la variable (pas de getter auto) - Les variables d’état persistent entre les différents appels de fonctions
3.2 Variables locales
# Variables locales - compilation et deploiement reel
LOCAL_VARS = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract LocalVariables {
uint256 public result;
function calculate(uint a, uint b) public returns (uint256) {
// Variables locales (en memoire, non stockees sur la blockchain)
uint256 sum = a + b;
uint256 product = a * b;
// Seul le resultat final est stocke
result = sum + product;
return result;
}
function computeOnly(uint a, uint b) public pure returns (uint256 sum, uint256 product) {
// pure : ni lecture ni ecriture sur la blockchain
sum = a + b;
product = a * b;
}
}
'''
print("--- Variables locales : temporaires, en memoire (non stockees) ---")
local_contract, receipt = compile_and_deploy(w3, LOCAL_VARS, deployer)
print(f" Deploye : {local_contract.address}")
print(f" calculate(3, 4) = {local_contract.functions.calculate(3, 4).call()}")
local_contract.functions.calculate(3, 4).transact({'from': deployer})
print(f" result sur la blockchain = {local_contract.functions.result().call()}")
s, p = local_contract.functions.computeOnly(5, 6).call()
print(f" computeOnly(5, 6) : sum={s}, product={p} (pas stocke)")--- Variables locales : temporaires, en memoire (non stockees) ---
Deploye: LocalVariables a 0x8A791620dd6260079BF849Dc5567aDC3F2FdC318
Deploye : 0x8A791620dd6260079BF849Dc5567aDC3F2FdC318
calculate(3, 4) = 19
result sur la blockchain = 19
computeOnly(5, 6) : sum=11, product=30 (pas stocke)
Interpretation : Variables locales vs état
Résultat obtenu : Les variables locales sont temporaires et stockees en mémoire, tandis que les variables d’état persistent sur la blockchain.
| Aspect | Variables locales | Variables d’état |
|---|---|---|
| Stockage | Memoire (RAM) | Blockchain (storage) |
| Durée | Temporaire (efface après l’appel) | Permanente |
| Coût gas | Faible | Élevé |
| Portee | Uniquement dans la fonction | Accessible partout dans le contrat |
Points clés : - calculate(3, 4) retourne 19 et stocke le résultat dans la variable d’état result - computeOnly(5, 6) utilise le mot-clé pure : ni lecture ni ecriture du storage, donc moins couteux en gas - Le mot-clé pure indique au compilateur que la fonction ne depend pas de l’état du contrat
3.3 Variables globales
# Variables globales - compilation et deploiement reel
GLOBAL_VARS = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract GlobalVariables {
event GlobalsRead(
address sender,
uint256 timestamp,
uint256 blockNumber,
uint256 gasLimit
);
function getGlobals() public returns (
address sender,
uint256 timestamp,
uint256 blockNumber,
uint256 gasLimit
) {
sender = msg.sender;
timestamp = block.timestamp;
blockNumber = block.number;
gasLimit = block.gaslimit;
emit GlobalsRead(sender, timestamp, blockNumber, gasLimit);
}
function getMsgValue() public payable returns (uint256) {
return msg.value;
}
}
'''
print("--- Variables globales : msg.sender, block.timestamp, block.number ---")
globals_contract, receipt = compile_and_deploy(w3, GLOBAL_VARS, deployer)
print(f" Deploye : {globals_contract.address}")
sender, ts, block_n, gas_lim = globals_contract.functions.getGlobals().call({'from': deployer})
print(f" msg.sender = {sender}")
print(f" block.timestamp = {ts}")
print(f" block.number = {block_n}")
print(f" block.gaslimit = {gas_lim:,}")--- Variables globales : msg.sender, block.timestamp, block.number ---
Deploye: GlobalVariables a 0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e
Deploye : 0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e
msg.sender = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
block.timestamp = 1780884575
block.number = 12
block.gaslimit = 30,000,000
Interpretation : les variables globales de la EVM
Les valeurs lues sont celles de la chaine anvil au moment de l’appel :
msg.sender = 0xf39Fd6...b92266— l’adresse qui a appele la fonction. C’est le compte #0 de la phrase mnemonique de test par defaut d’anvil/Hardhat (well-known test account), ici ledeployer. Critique en sécurité : tout controle d’accès repose sur cette valeur (require(msg.sender == owner)).block.timestamp = 1780884575— horodatage Unix du bloc courant (secondes depuis 1970-01-01). La EVM n’expose pasnowhors du bloc courant : c’est la seule notion de temps disponible on-chain, avec une granularite de l’ordre de la seconde (un bloc ~= 12s sur Ethereum mainnet).block.number = 12— hauteur du bloc courant (le 12e bloc mine sur cette chaine locale).block.gaslimit = 30 000 000— quantite maximale de gaz autorisee par bloc (30M, standard Ethereum mainnet).
Ces variables sont des entrées implicites de toute fonction : elles ne figurent pas dans la signature mais caracterisent le contexte d’execution. Un même appel par deux comptes differents produit deux msg.sender differents — d’ou l’importance de ne jamais les supposer constantes.
4. Conversions de types
# Conversions
CONVERSION_EXAMPLE = '''
contract TypeConversions {
// Conversion explicite
function uintToInt(uint256 a) public pure returns (int256) {
return int256(a);
}
// Conversion address to payable
function toPayable(address addr) public pure returns (address payable) {
return payable(addr);
}
// String to bytes
function stringToBytes(string memory s) public pure returns (bytes memory) {
return bytes(s);
}
// Address to uint (pour comparaison)
function addressToUint(address addr) public pure returns (uint160) {
return uint160(addr);
}
}
'''
print("Conversions de types Solidity:")
print(CONVERSION_EXAMPLE)Conversions de types Solidity:
contract TypeConversions {
// Conversion explicite
function uintToInt(uint256 a) public pure returns (int256) {
return int256(a);
}
// Conversion address to payable
function toPayable(address addr) public pure returns (address payable) {
return payable(addr);
}
// String to bytes
function stringToBytes(string memory s) public pure returns (bytes memory) {
return bytes(s);
}
// Address to uint (pour comparaison)
function addressToUint(address addr) public pure returns (uint160) {
return uint160(addr);
}
}
Interpretation : les conversions de types en Solidity
Les quatre conversions couvrent les cas les plus frequents :
uint256 -> int256(explicite) — toujours sure (tout entier non signe tient dans le signe) ; la reciproqueint256 -> uint256est dangereuse (un negatif devient un entier positif immense).address -> address payable— nécessaire pour appeler.transfer()ou.send(). Depuis Solidity 0.8,payable(addr)est la syntaxe explicite ; la conversion inverse est automatique.string -> bytes—bytesest la representation bas-niveau ; la conversion est gratuite carstringest déjà encodee en UTF-8 sous le capot.address -> uint160— une adresse Ethereum n’est qu’un entier de 160 bits ; cette conversion sert à comparer ou trier des adresses (mapping dense, arithmétique).
Toutes ces fonctions sont marquées pure : elles ne lisent ni n’ecrivent l’état du contrat, elles transforment juste leurs arguments. Un appel pure est donc gratuit hors-transaction (view call local, pas de frais de gaz).
5. Exemples guidés et Exercices
Exemple guide 1 : Contrat SimpleStorage
Un contrat qui stocke et récupère une valeur uint256. Solution complète compilée et déployée.
# Exemple guide 1 : Contrat SimpleStorage
EXERCICE_1 = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract SimpleStorage {
uint256 private data;
function set(uint256 _value) public {
data = _value;
}
function get() public view returns (uint256) {
return data;
}
}
'''
# Compilation et deploiement reel sur anvil
simplestorage, receipt = compile_and_deploy(w3, EXERCICE_1, deployer)
print(f"Valeur initiale : {simplestorage.functions.get().call()}") # 0
simplestorage.functions.set(42).transact({'from': deployer})
print(f"Apres set(42) : {simplestorage.functions.get().call()}") # 42
simplestorage.functions.set(1337).transact({'from': deployer})
print(f"Apres set(1337) : {simplestorage.functions.get().call()}") # 1337Deploye: SimpleStorage a 0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0
Valeur initiale : 0
Apres set(42) : 42
Apres set(1337) : 1337
Analyse : Le contrat SimpleStorage utilise une variable d’état uint256 private data pour stocker une valeur. La fonction set() modifie le storage (coûte du gas), tandis que get() est view (lecture seule, gratuite). La valeur initiale d’un uint256 est 0 (valeur par defaut).
Exemple guide 2 : Contrat Owner
Un contrat qui stocke l’adresse du propriétaire et permet de la changer. Solution complète compilée et déployée.
# Exemple guide 2 : Contrat Owner
EXERCICE_2 = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract Owner {
address public owner;
constructor() {
owner = msg.sender;
}
function changeOwner(address _newOwner) public {
owner = _newOwner;
}
}
'''
# Compilation et deploiement reel sur anvil
owner, receipt = compile_and_deploy(w3, EXERCICE_2, deployer)
initial_owner = owner.functions.owner().call()
print(f"Owner initial : {initial_owner}")
print(f"Deployer : {deployer}")
print(f"Sont identiques : {initial_owner.lower() == deployer.lower()}")
new_owner = w3.eth.accounts[1]
owner.functions.changeOwner(new_owner).transact({'from': deployer})
print(f"\nApres changeOwner({new_owner[:10]}...)")
print(f"Nouvel owner : {owner.functions.owner().call()}")
print(f"Changement OK : {owner.functions.owner().call().lower() == new_owner.lower()}")Deploye: Owner a 0x0B306BF915C4d645ff596e518fAf3F9669b97016
Owner initial : 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deployer : 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Sont identiques : True
Apres changeOwner(0x70997970...)
Nouvel owner : 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
Changement OK : True
Analyse : Le contrat Owner utilise le pattern ownership : le constructor capture msg.sender (l’adresse du deployeur) comme owner initial. La fonction changeOwner() met à jour cette adresse. Notez que dans cette version simplifiee, n’importe qui peut changer l’owner — un vrai contrat ajouterait un require(msg.sender == owner).
Exercice 1 : Contrat CounterEvents
Créez un compteur qui emet un événement a chaque incrementation. Les events sont le mécanisme de logging on-chain en Solidity.
Objectifs : 1. Définir un event avec des paramètres indexes 2. Emettre un event avec emit dans une fonction 3. Capturer l’event dans les logs de transaction
# Exercice 1 : Contrat CounterEvents
# TODO etudiant : implementer un compteur qui emet un evenement a chaque incrementation
# Etape 1 : Definir un event Counted(address indexed caller, uint256 newCount)
# Etape 2 : Implementer increment() qui emet l'event
# Etape 3 : Implementer getCount() view returns (uint256)
EXERCICE_3 = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract CounterEvents {
// TODO etudiant : event, variable d'etat, et fonctions
}
"""
print("Exercice a completer")Exercice a completer
Exercice 2 : Contrat OwnedCounter
Combinez les concepts des exemples précédents : créez un compteur qui ne peut être incremente que par le propriétaire du contrat.
Objectifs : 1. Combiner variables d’état (uint256, address) et contrôle d’accès 2. Utiliser require() pour proteger une fonction 3. Deployer et tester le contrôle d’ownership sur anvil
# Exercice 2 : Contrat OwnedCounter
# TODO etudiant : implementer un compteur avec controle d'ownership
# Etape 1 : Definir une variable d'etat uint256 public count et address public owner
# Etape 2 : Capturer msg.sender dans le constructor
# Etape 3 : Implementer increment() avec require(msg.sender == owner, "Not owner")
# Etape 4 : Implementer getCount() view returns (uint256)
EXERCICE_4 = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract OwnedCounter {
// TODO etudiant : variables d'etat et fonctions
}
"""
print("Exercice a completer")Exercice a completer
Exercice 3 : Contrat NumberBox avec conversions de types
Mettez en pratique les conversions de types (section 4) en creant un contrat qui stocke un entier et expose ses representations dans plusieurs types.
Objectifs : 1. Stocker int256 public value 2. Implementer setValue(int256 _v) 3. Implementer toUint() public view returns (uint256) avec conversion uint256(value)
Indice : uint256(value) convertit explicitement. Convertir un negatif donne un très grand nombre.
# Exercice 3 : Contrat NumberBox avec conversions de types
# TODO etudiant : implementer un contrat qui stocke un int256 et le convertit en uint256
# Etape 1 : Definir int256 public value
# Etape 2 : Implementer setValue(int256 _v)
# Etape 3 : Implementer toUint() public view returns (uint256) avec uint256(value)
EXERCICE_5 = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract NumberBox {
// TODO etudiant : variable d'etat int256, setValue et toUint
}
"""
# numberbox, receipt = compile_and_deploy(w3, EXERCICE_5, deployer)
# numberbox.functions.setValue(42).transact({'from': deployer})
# print("toUint() =", numberbox.functions.toUint().call())
print("Exercice a completer")Exercice a completer
6. Résumé
| Type | Description | Exemple |
|---|---|---|
bool |
Booléen | true, false |
uint256 |
Entier non signe | 42, 1000000 |
int256 |
Entier signe | -42, 42 |
address |
Adresse Ethereum | 0x1234... |
bytes32 |
Bytes fixes | hex"abcd" |
string |
Chaîne UTF-8 | "Hello" |
Notebook suivant : SC-04-Functions-State-Python
Résumé et perspectives
Ce notebook a posé les fondations du langage Solidity en explorant ses types de données fondamentaux (booléens, entiers signés et non signés, adresses Ethereum, bytes et strings) et les mécanismes de variables (d’état persistantes en storage, locales temporaires en mémoire, globales comme msg.sender et block.timestamp). Chaque type a été compilé, déployé et exécuté réellement sur la blockchain locale anvil, illustrant concrètement le coût de stockage et le comportement de chaque opérateur.
La distinction entre variables d’état et variables locales est centrale dans la programmation Solidity : les premières persistent sur la blockchain entre les appels de fonction mais coûtent du gas, tandis que les secondes sont gratuites mais temporaires. Les variables globales comme msg.sender, msg.value et block.timestamp sont les points d’ancrage entre le contexte d’exécution d’une transaction et la logique du contrat. Les conversions de types (uint256 vers int256, address vers address payable, string vers bytes) complètent ce socle en permettant les manipulations nécessaires à la logique métier.
Ce socle de types et de variables est le prerequisite direct pour le notebook suivant, SC-04-Functions-State-Python, qui approfondit les data locations (storage, memory, calldata), la visibilité des fonctions et les modificateurs (view, pure, payable), ainsi que les custom modifiers.
Exercice 4 : Contrat MessageStore (string et bytes)
Reutilisez les types string / bytes (section 2.4) pour créer un contrat qui stocke un message et expose sa longueur en octets.
Indice : bytes(message).length donne la longueur en octets (pas en caractères).
# Exercice 4 : Contrat MessageStore (string et bytes)
# TODO etudiant : implementer un contrat qui stocke un string et expose sa longueur en octets
# Etape 1 : Definir string public message
# Etape 2 : Implementer setMessage(string memory _msg)
# Etape 3 : Implementer byteLength() view returns (uint256) avec bytes(message).length
EXERCICE_6 = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract MessageStore {
// TODO etudiant : variable d'etat string, setMessage et byteLength
}
"""
# msgstore, receipt = compile_and_deploy(w3, EXERCICE_6, deployer)
# msgstore.functions.setMessage("Solidity").transact({'from': deployer})
# print("byteLength() =", msgstore.functions.byteLength().call())
print("Exercice a completer")Exercice a completer
Exercice 5 : Contrat Toggle (booléens et variables d’état)
Combinez booléens (section 2.1) et variables d’état (section 3.1) pour créer un interrupteur on/off avec compteur de bascules.
Indice : isOn = !isOn inverse le booléen. returns (bool, uint256) pour multi-return.
# Exercice 5 : Contrat Toggle (booleens et variables d'etat)
# TODO etudiant : implementer un interrupteur on/off avec compteur de bascules
# Etape 1 : Definir bool public isOn et uint256 public toggleCount
# Etape 2 : Implementer toggle() qui inverse isOn et incremente toggleCount
# Etape 3 : Implementer status() view returns (bool, uint256)
EXERCICE_7 = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract Toggle {
// TODO etudiant : variables d'etat bool/uint256, toggle et status
}
"""
# toggle_c, receipt = compile_and_deploy(w3, EXERCICE_7, deployer)
# toggle_c.functions.toggle().transact({'from': deployer})
# toggle_c.functions.toggle().transact({'from': deployer})
# etat, count = toggle_c.functions.status().call()
# print(f"isOn={etat}, toggleCount={count}")
print("Exercice a completer")Exercice a completer