bclab 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bclab-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.5
2
+ Name: bclab
3
+ Version: 1.0.0
4
+ Summary: Blockchain Technology lab (CS441): menu with steps and code for 8 experiments
5
+ Requires-Python: >=3.8
6
+ Requires-Dist: cryptography
7
+ Description-Content-Type: text/markdown
8
+
9
+ # bclab
10
+
11
+ Blockchain Technology lab menu. Lists 8 experiments, shows steps and code, and can run or copy the files.
12
+
13
+ pipx install bclab
14
+ bclab
bclab-1.0.0/README.md ADDED
@@ -0,0 +1,6 @@
1
+ # bclab
2
+
3
+ Blockchain Technology lab menu. Lists 8 experiments, shows steps and code, and can run or copy the files.
4
+
5
+ pipx install bclab
6
+ bclab
@@ -0,0 +1,17 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "bclab"
7
+ version = "1.0.0"
8
+ description = "Blockchain Technology lab (CS441): menu with steps and code for 8 experiments"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ dependencies = ["cryptography"]
12
+
13
+ [project.scripts]
14
+ bclab = "bclab.cli:main"
15
+
16
+ [tool.hatch.build.targets.wheel]
17
+ packages = ["src/bclab"]
File without changes
@@ -0,0 +1,101 @@
1
+ """Blockchain lab menu: pick an experiment, see its steps and code, copy or run the files."""
2
+ import os
3
+ import shutil
4
+ import subprocess
5
+ import sys
6
+
7
+ try:
8
+ sys.stdout.reconfigure(encoding="utf-8")
9
+ except Exception:
10
+ pass
11
+
12
+ ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
13
+ CODE_EXT = (".py", ".sol")
14
+
15
+
16
+ def experiments():
17
+ return sorted(
18
+ (d for d in os.listdir(ROOT)
19
+ if d.lower().startswith("exp") and os.path.isdir(os.path.join(ROOT, d))),
20
+ key=lambda d: int("".join(c for c in d.split("_")[0] if c.isdigit()) or 0),
21
+ )
22
+
23
+
24
+ def read(path):
25
+ with open(path, encoding="utf-8") as f:
26
+ return f.read()
27
+
28
+
29
+ def show(folder):
30
+ base = os.path.join(ROOT, folder)
31
+ bar = "=" * 70
32
+ print(f"\n{bar}\n{folder}\n{bar}\n")
33
+ steps = os.path.join(base, "steps.md")
34
+ if os.path.exists(steps):
35
+ print(read(steps))
36
+ for name in sorted(os.listdir(base)):
37
+ if name.endswith(CODE_EXT):
38
+ print(f"\n{'-' * 70}\nFILE: {name}\n{'-' * 70}")
39
+ print(read(os.path.join(base, name)))
40
+
41
+
42
+ def copy_files(folder):
43
+ base = os.path.join(ROOT, folder)
44
+ dest = input(f"Copy into which folder? [Enter = {os.getcwd()}]: ").strip().strip('"') or os.getcwd()
45
+ os.makedirs(dest, exist_ok=True)
46
+ for name in os.listdir(base):
47
+ shutil.copy2(os.path.join(base, name), os.path.join(dest, name))
48
+ print(f" created {os.path.join(dest, name)}")
49
+
50
+
51
+ def run_python(folder):
52
+ base = os.path.join(ROOT, folder)
53
+ pys = [n for n in sorted(os.listdir(base)) if n.endswith(".py")]
54
+ if not pys:
55
+ print("No Python file in this experiment (use Remix for Solidity).")
56
+ return
57
+ for n in pys:
58
+ print(f"\n>>> python {n}\n")
59
+ subprocess.run([sys.executable, n], cwd=base)
60
+
61
+
62
+ def action_menu(folder):
63
+ while True:
64
+ print("\n[s] show again [r] run Python file [c] copy files to a folder [b] back [q] quit")
65
+ a = input("> ").strip().lower()
66
+ if a == "s":
67
+ show(folder)
68
+ elif a == "r":
69
+ run_python(folder)
70
+ elif a == "c":
71
+ copy_files(folder)
72
+ elif a == "b":
73
+ return True
74
+ elif a == "q":
75
+ return False
76
+
77
+
78
+ def main():
79
+ while True:
80
+ exps = experiments()
81
+ print("\n" + "=" * 70 + "\n BLOCKCHAIN TECHNOLOGY LAB (CS441) - EXPERIMENTS\n" + "=" * 70)
82
+ for i, e in enumerate(exps, 1):
83
+ print(f" {i}. {e}")
84
+ print(" q. Quit")
85
+ choice = input("\nSelect experiment number: ").strip().lower()
86
+ if choice == "q":
87
+ return
88
+ if not choice.isdigit() or not 1 <= int(choice) <= len(exps):
89
+ print("Invalid choice.")
90
+ continue
91
+ folder = exps[int(choice) - 1]
92
+ show(folder)
93
+ if not action_menu(folder):
94
+ return
95
+
96
+
97
+ if __name__ == "__main__":
98
+ try:
99
+ main()
100
+ except (KeyboardInterrupt, EOFError):
101
+ print()
@@ -0,0 +1,21 @@
1
+ # Exp 1: Fundamentals of Blockchain Technology (theory)
2
+
3
+ **Aim:** To study the fundamental concepts of blockchain, its history, evolution and key characteristics.
4
+
5
+ ## Points to explain
6
+ 1. **Definition:** A blockchain is a distributed, append-only ledger made of blocks. Each block stores data plus the hash of the previous block.
7
+ 2. **Decentralization:** No single central authority. Many nodes hold a copy of the ledger.
8
+ 3. **Transparency:** All participants can view the recorded transactions.
9
+ 4. **Immutability:** Changing an old block changes its hash and breaks every later block, so tampering is easy to detect.
10
+ 5. **History / evolution:**
11
+ - 2008: Bitcoin whitepaper (Satoshi Nakamoto)
12
+ - 2009: Bitcoin network launched (Blockchain 1.0, digital currency)
13
+ - 2015: Ethereum launched with smart contracts (Blockchain 2.0)
14
+ - Later: enterprise use in supply chain, finance, healthcare (Blockchain 3.0)
15
+ 6. **Types:** Public, private, consortium.
16
+ 7. **Applications:** Supply chain, finance, healthcare, digital identity, government records.
17
+
18
+ ## Viva questions
19
+ - What makes a blockchain immutable?
20
+ - Difference between public and private blockchains?
21
+ - What is a node?
@@ -0,0 +1,22 @@
1
+ # Exp 2: Blockchain Data Structures and Consensus Mechanisms (theory)
2
+
3
+ **Aim:** To study blocks, chains, Merkle trees and the PoW / PoS consensus mechanisms.
4
+
5
+ ## Points to explain
6
+ 1. **Block:** header (previous hash, timestamp, nonce, Merkle root) + body (transactions). In our Python code: index, timestamp, data, previous_hash, nonce, hash.
7
+ 2. **Chain:** each block stores the previous block's hash. The first block is the **Genesis block** with previous_hash = "0".
8
+ 3. **Merkle tree:** hash each transaction, pair the hashes and hash them again, repeat until one **Merkle root** remains. A change in any transaction changes the root. It allows quick verification of one transaction.
9
+ 4. **Proof of Work (PoW):** miners try nonces until the block hash meets a difficulty target (e.g. leading zeros). Secure, but uses a lot of energy. Used by Bitcoin.
10
+ 5. **Proof of Stake (PoS):** validators are chosen according to the coins they stake. Dishonest validators lose their stake (slashing). Energy efficient. Used by Ethereum now.
11
+
12
+ ## PoW vs PoS
13
+ | | PoW | PoS |
14
+ |---|---|---|
15
+ | Selection | Computing power | Staked coins |
16
+ | Energy | High | Low |
17
+ | Example | Bitcoin | Ethereum |
18
+
19
+ ## Viva questions
20
+ - What is the role of the nonce?
21
+ - Why use a Merkle tree?
22
+ - What is a 51% attack?
@@ -0,0 +1,33 @@
1
+ import hashlib
2
+
3
+ def compute_sha256(data: str) -> str:
4
+ h = hashlib.sha256()
5
+ h.update(data.encode("utf-8"))
6
+ return h.hexdigest()
7
+
8
+ def count_bit_difference(h1: str, h2: str) -> int:
9
+ b1 = bin(int(h1, 16))[2:].zfill(len(h1) * 4)
10
+ b2 = bin(int(h2, 16))[2:].zfill(len(h2) * 4)
11
+ return sum(x != y for x, y in zip(b1, b2))
12
+
13
+ def avalanche(a: str, b: str):
14
+ h1, h2 = compute_sha256(a), compute_sha256(b)
15
+ print(f"Input 1 : {a!r}\nHash 1 : {h1}\n")
16
+ print(f"Input 2 : {b!r}\nHash 2 : {h2}\n")
17
+ d = count_bit_difference(h1, h2)
18
+ print(f"Differing bits: {d}/256 ({d/256*100:.2f}%)")
19
+ print("-" * 60)
20
+
21
+ for t in ["Hello, World!", "RIT Sangli", "Password123", ""]:
22
+ print(f"Input: {t!r}\nHash : {compute_sha256(t)}\n")
23
+
24
+ avalanche("Hello, World!", "Hello, World?")
25
+ avalanche("password", "Password")
26
+ avalanche("RIT Sangli", "RIT Sangli ")
27
+
28
+ print("Type a string to hash (empty line to quit):")
29
+ while True:
30
+ s = input("> ")
31
+ if not s:
32
+ break
33
+ print(compute_sha256(s))
@@ -0,0 +1,26 @@
1
+ # Exp 3: SHA-256 Hashing in Python
2
+
3
+ **Aim:** To implement SHA-256 hashing in Python and observe how changes in input affect the hash (avalanche effect).
4
+
5
+ **Requirements:** Python 3.8+ (uses only the standard library `hashlib`).
6
+
7
+ ## Steps
8
+ 1. Open a terminal in this folder.
9
+ 2. Import `hashlib`.
10
+ 3. Write `compute_sha256()` that encodes the string to UTF-8 and returns `hexdigest()`.
11
+ 4. Hash a few sample strings (including the empty string).
12
+ 5. Write `count_bit_difference()` that converts both hashes to binary and counts differing bits.
13
+ 6. Hash two nearly identical inputs (one character changed, case changed, trailing space) and compare. About 50% of the 256 bits should differ.
14
+ 7. Run the program.
15
+
16
+ ```
17
+ python sha256_demo.py
18
+ ```
19
+
20
+ **Code file:** `sha256_demo.py`
21
+
22
+ ## Key points for viva
23
+ - SHA-256 always gives a 256-bit (64 hex character) output, whatever the input size.
24
+ - It is deterministic, one-way and collision resistant.
25
+ - Avalanche effect: a tiny input change changes about half the output bits.
26
+ - Hashing is not encryption: it cannot be reversed.
@@ -0,0 +1,57 @@
1
+ import hashlib, json, time
2
+
3
+ class Block:
4
+ def __init__(self, index, data, previous_hash, timestamp=None, nonce=0):
5
+ self.index = index
6
+ self.timestamp = timestamp or time.time()
7
+ self.data = data
8
+ self.previous_hash = previous_hash
9
+ self.nonce = nonce
10
+ self.hash = self.compute_hash()
11
+
12
+ def compute_hash(self):
13
+ s = json.dumps({"index": self.index, "timestamp": self.timestamp,
14
+ "data": self.data, "previous_hash": self.previous_hash,
15
+ "nonce": self.nonce}, sort_keys=True)
16
+ return hashlib.sha256(s.encode()).hexdigest()
17
+
18
+ class Blockchain:
19
+ def __init__(self, difficulty=2):
20
+ self.difficulty = difficulty
21
+ self.chain = [Block(0, "Genesis Block", "0")]
22
+
23
+ def proof_of_work(self, block):
24
+ target = "0" * self.difficulty
25
+ while not block.hash.startswith(target):
26
+ block.nonce += 1
27
+ block.hash = block.compute_hash()
28
+ return block
29
+
30
+ def add_block(self, data):
31
+ prev = self.chain[-1]
32
+ blk = self.proof_of_work(Block(prev.index + 1, data, prev.hash))
33
+ self.chain.append(blk)
34
+
35
+ def is_chain_valid(self):
36
+ for i in range(1, len(self.chain)):
37
+ cur, prev = self.chain[i], self.chain[i - 1]
38
+ if cur.hash != cur.compute_hash():
39
+ print(f"Block {cur.index} tampered (hash mismatch).")
40
+ return False
41
+ if cur.previous_hash != prev.hash:
42
+ print(f"Block {cur.index} not linked correctly.")
43
+ return False
44
+ if not cur.hash.startswith("0" * self.difficulty):
45
+ print(f"Block {cur.index} fails Proof-of-Work.")
46
+ return False
47
+ return True
48
+
49
+ c = Blockchain(difficulty=3)
50
+ c.add_block("Alice pays Bob 10 coins")
51
+ c.add_block("Bob pays Charlie 5 coins")
52
+ c.add_block("Charlie pays Dave 2 coins")
53
+ for b in c.chain:
54
+ print(f"Block #{b.index} | prev={b.previous_hash[:12]}... | hash={b.hash[:12]}... | nonce={b.nonce} | {b.data}")
55
+ print("Chain valid before tampering?", c.is_chain_valid())
56
+ c.chain[1].data = "Alice pays Bob 1000 coins"
57
+ print("Chain valid after tampering? ", c.is_chain_valid())
@@ -0,0 +1,35 @@
1
+ # Exp 4: Simple Blockchain using Cryptographic Hash Linking
2
+
3
+ **Aim:** To build a simple blockchain in Python by linking blocks with SHA-256 hashes and verify chain integrity by detecting tampering.
4
+
5
+ **Requirements:** Python 3.8+; standard libraries `hashlib`, `json`, `time`.
6
+
7
+ ## Algorithm / Steps
8
+ 1. Define a `Block` class with: index, timestamp, data, previous_hash, nonce, hash.
9
+ 2. Compute a block's hash as SHA-256 of its serialized fields (use `json.dumps(..., sort_keys=True)`).
10
+ 3. Create the **Genesis block** (index 0, previous_hash = "0").
11
+ 4. To add a block: take the previous block's hash as `previous_hash`, then run **Proof of Work**: increase `nonce` until the hash starts with the required number of zeros.
12
+ 5. Append the block to the chain.
13
+ 6. To verify the chain, for each block from index 1: (a) recompute the hash and compare with the stored hash, (b) check `previous_hash` equals the previous block's hash, (c) check the PoW difficulty is satisfied.
14
+ 7. If any check fails, report the chain as invalid; otherwise valid.
15
+ 8. Tamper with block 1's data and validate again.
16
+
17
+ ```
18
+ python simple_blockchain.py
19
+ ```
20
+
21
+ **Code file:** `simple_blockchain.py`
22
+
23
+ ## Expected output
24
+ ```
25
+ Chain valid before tampering? True
26
+ Block 1 tampered (hash mismatch).
27
+ Chain valid after tampering? False
28
+ ```
29
+
30
+ ## Viva questions
31
+ 1. Why does changing data in one block invalidate all subsequent blocks?
32
+ 2. What is the role of the Genesis block?
33
+ 3. How does Proof of Work make tampering expensive?
34
+ 4. If an attacker recomputes all downstream hashes, how does a real network defend? (distributed consensus, the honest majority holds the longest valid chain)
35
+ 5. Hashing vs encryption? (hashing is one-way, encryption is reversible with a key)
@@ -0,0 +1,28 @@
1
+ # pip install cryptography
2
+ from cryptography.hazmat.primitives.asymmetric import ec
3
+ from cryptography.hazmat.primitives import hashes, serialization
4
+ from cryptography.exceptions import InvalidSignature
5
+
6
+ private_key = ec.generate_private_key(ec.SECP256K1())
7
+ public_key = private_key.public_key()
8
+
9
+ print("Private key (PEM):")
10
+ print(private_key.private_bytes(serialization.Encoding.PEM,
11
+ serialization.PrivateFormat.PKCS8, serialization.NoEncryption()).decode())
12
+ print("Public key (PEM):")
13
+ print(public_key.public_bytes(serialization.Encoding.PEM,
14
+ serialization.PublicFormat.SubjectPublicKeyInfo).decode())
15
+
16
+ message = b"Alice pays Bob 10 coins"
17
+ signature = private_key.sign(message, ec.ECDSA(hashes.SHA256()))
18
+ print("Signature:", signature.hex())
19
+
20
+ def verify(msg):
21
+ try:
22
+ public_key.verify(signature, msg, ec.ECDSA(hashes.SHA256()))
23
+ return True
24
+ except InvalidSignature:
25
+ return False
26
+
27
+ print("Original message valid? ", verify(message))
28
+ print("Tampered message valid? ", verify(b"Alice pays Bob 1000 coins"))
@@ -0,0 +1,34 @@
1
+ # Exp 5: Public/Private Key Generation and Digital Signature Simulation
2
+
3
+ **Aim:** To generate a public/private key pair and simulate signing and verifying a message in Python.
4
+
5
+ **Requirements:** Python 3.8+ and the `cryptography` package.
6
+
7
+ ```
8
+ pip install cryptography
9
+ ```
10
+
11
+ ## Steps
12
+ 1. Generate a private key using ECDSA on the secp256k1 curve (the same curve Bitcoin and Ethereum use).
13
+ 2. Derive the public key from the private key.
14
+ 3. Print both keys in PEM format.
15
+ 4. Sign a message with the **private** key (hash with SHA-256, then sign).
16
+ 5. Verify the signature with the **public** key: it should print True.
17
+ 6. Change the message and verify again: it should print False (InvalidSignature).
18
+
19
+ ```
20
+ python sign_verify.py
21
+ ```
22
+
23
+ **Code file:** `sign_verify.py`
24
+
25
+ ## Expected output
26
+ ```
27
+ Original message valid? True
28
+ Tampered message valid? False
29
+ ```
30
+
31
+ ## Viva questions
32
+ - Why is the private key kept secret and the public key shared?
33
+ - What does a digital signature give us? (authentication, integrity, non-repudiation)
34
+ - How is a wallet address derived from a public key?
@@ -0,0 +1,14 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.19;
3
+
4
+ contract HelloWorld {
5
+ string private message = "Hello, World!";
6
+
7
+ function getMessage() public view returns (string memory) {
8
+ return message;
9
+ }
10
+
11
+ function setMessage(string memory _msg) public {
12
+ message = _msg;
13
+ }
14
+ }
@@ -0,0 +1,23 @@
1
+ # Exp 6: "Hello World" Smart Contract in Solidity (Remix IDE)
2
+
3
+ **Aim:** To write, compile and deploy a basic Hello World smart contract on Remix IDE.
4
+
5
+ **Requirements:** A web browser and https://remix.ethereum.org
6
+
7
+ ## Steps
8
+ 1. Open https://remix.ethereum.org
9
+ 2. In the File Explorer, create a new file `HelloWorld.sol`.
10
+ 3. Paste the code from `HelloWorld.sol` in this folder.
11
+ 4. Open the **Solidity Compiler** tab, select compiler **0.8.19** (or a compatible 0.8.x) and click **Compile HelloWorld.sol**.
12
+ 5. Open the **Deploy & Run Transactions** tab and set Environment to **Remix VM**.
13
+ 6. Select the `HelloWorld` contract and click **Deploy**.
14
+ 7. Under *Deployed Contracts*, click `getMessage`. It returns "Hello, World!".
15
+ 8. Type a new text in `setMessage`, click **transact**, then call `getMessage` again to see the changed message.
16
+
17
+ **Code file:** `HelloWorld.sol`
18
+
19
+ ## Viva questions
20
+ - What is a smart contract?
21
+ - What does `pragma solidity` do?
22
+ - Difference between `view` functions and functions that change state? (view reads only and costs no gas when called directly)
23
+ - What is the Remix VM?
@@ -0,0 +1,35 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.19;
3
+
4
+ contract SimpleToken {
5
+ string public name = "SimpleToken";
6
+ string public symbol = "STK";
7
+ uint8 public decimals = 18;
8
+ uint256 public totalSupply;
9
+ address public owner;
10
+ mapping(address => uint256) public balanceOf;
11
+
12
+ event Transfer(address indexed from, address indexed to, uint256 value);
13
+
14
+ constructor(uint256 _initialSupply) {
15
+ owner = msg.sender;
16
+ totalSupply = _initialSupply * (10 ** uint256(decimals));
17
+ balanceOf[owner] = totalSupply;
18
+ emit Transfer(address(0), owner, totalSupply);
19
+ }
20
+
21
+ // Transfer tokens from caller to another address
22
+ function transfer(address _to, uint256 _value) public returns (bool success) {
23
+ require(_to != address(0), "Invalid address");
24
+ require(balanceOf[msg.sender] >= _value, "Insufficient balance");
25
+ balanceOf[msg.sender] -= _value;
26
+ balanceOf[_to] += _value;
27
+ emit Transfer(msg.sender, _to, _value);
28
+ return true;
29
+ }
30
+
31
+ // Helper to check balance of any account
32
+ function getBalance(address _account) public view returns (uint256) {
33
+ return balanceOf[_account];
34
+ }
35
+ }
@@ -0,0 +1,44 @@
1
+ # Exp 7: Simple Token Transfer Smart Contract
2
+
3
+ **Aim:** To implement a simple token transfer contract in Solidity, deploy it on Remix VM and verify balance changes between accounts.
4
+
5
+ ## Steps
6
+ 1. Open https://remix.ethereum.org
7
+ 2. Create `SimpleToken.sol` and paste the code from this folder.
8
+ 3. **Compile:** Solidity Compiler tab, version 0.8.19, click Compile.
9
+ 4. **Environment:** Deploy & Run tab, select **Remix VM (Cancun)**.
10
+ 5. **Constructor argument:** select `SimpleToken` and enter `1000` next to Deploy.
11
+ 6. Click **Deploy**. The contract appears under *Deployed Contracts*.
12
+
13
+ ### Check initial balances
14
+ 7. Copy Account 0's address (from the Account dropdown) and call `getBalance(<Account0>)`. Expected: `1000000000000000000000` (1000 tokens, 18 decimals).
15
+ 8. Call `getBalance(<Account1>)`. Expected: `0`.
16
+
17
+ ### Transfer
18
+ 9. Keep **Account 0** selected as the sender.
19
+ 10. In `transfer`, enter `_to` = Account 1's address and `_value` = `100000000000000000000` (100 tokens). Click **transact**.
20
+
21
+ ### Verify
22
+ 11. Call `getBalance` for both accounts again.
23
+
24
+ | Account | Before | After |
25
+ |---|---|---|
26
+ | Account 0 (sender) | 1000 STK | 900 STK |
27
+ | Account 1 (receiver) | 0 STK | 100 STK |
28
+
29
+ 12. Expand the transaction in the Remix console and check the `Transfer` event (from, to, value).
30
+
31
+ ### Negative test (include in the report)
32
+ 13. Try to transfer more than the sender's balance. Remix reverts with **"Insufficient balance"**.
33
+
34
+ **Code file:** `SimpleToken.sol`
35
+
36
+ ## Notes
37
+ - Amounts are in the smallest unit because of 18 decimals (100 tokens = 100 * 10^18).
38
+ - `require()` blocks sending to the zero address and sending more than the balance.
39
+ - `mapping(address => uint256)` stores balances.
40
+
41
+ ## Viva questions
42
+ - What is the use of `msg.sender`?
43
+ - What is an event and why emit `Transfer`?
44
+ - Why are 18 decimals used?
@@ -0,0 +1,14 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.19;
3
+
4
+ contract HelloWorld {
5
+ string private message = "Hello, World!";
6
+
7
+ function getMessage() public view returns (string memory) {
8
+ return message;
9
+ }
10
+
11
+ function setMessage(string memory _msg) public {
12
+ message = _msg;
13
+ }
14
+ }
@@ -0,0 +1,35 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.19;
3
+
4
+ contract SimpleToken {
5
+ string public name = "SimpleToken";
6
+ string public symbol = "STK";
7
+ uint8 public decimals = 18;
8
+ uint256 public totalSupply;
9
+ address public owner;
10
+ mapping(address => uint256) public balanceOf;
11
+
12
+ event Transfer(address indexed from, address indexed to, uint256 value);
13
+
14
+ constructor(uint256 _initialSupply) {
15
+ owner = msg.sender;
16
+ totalSupply = _initialSupply * (10 ** uint256(decimals));
17
+ balanceOf[owner] = totalSupply;
18
+ emit Transfer(address(0), owner, totalSupply);
19
+ }
20
+
21
+ // Transfer tokens from caller to another address
22
+ function transfer(address _to, uint256 _value) public returns (bool success) {
23
+ require(_to != address(0), "Invalid address");
24
+ require(balanceOf[msg.sender] >= _value, "Insufficient balance");
25
+ balanceOf[msg.sender] -= _value;
26
+ balanceOf[_to] += _value;
27
+ emit Transfer(msg.sender, _to, _value);
28
+ return true;
29
+ }
30
+
31
+ // Helper to check balance of any account
32
+ function getBalance(address _account) public view returns (uint256) {
33
+ return balanceOf[_account];
34
+ }
35
+ }
@@ -0,0 +1,28 @@
1
+ # Exp 8: Deploy a Smart Contract on the Ethereum Sepolia Testnet (MetaMask + Etherscan)
2
+
3
+ **Aim:** To deploy a smart contract on the Sepolia testnet using MetaMask and verify the transaction on Etherscan.
4
+
5
+ **Requirements:** Chrome/Edge/Firefox, the MetaMask extension, Remix IDE, free Sepolia test ETH.
6
+
7
+ ## Steps
8
+ 1. Install the **MetaMask** extension (https://metamask.io) and create (or import) a wallet. Save the recovery phrase safely and never share it.
9
+ 2. In MetaMask open the network dropdown, enable **Show test networks** and select **Sepolia**.
10
+ 3. Get free Sepolia test ETH from a faucet (e.g. Google Cloud Web3 faucet or Alchemy Sepolia faucet) by pasting your wallet address. Wait until the balance shows in MetaMask.
11
+ 4. Open https://remix.ethereum.org and use the contract from this folder (`HelloWorld.sol`, or `SimpleToken.sol`).
12
+ 5. **Compile** it with 0.8.19.
13
+ 6. In Deploy & Run Transactions set Environment to **Injected Provider - MetaMask**. Check that the network shows *Sepolia (11155111)* and your account is selected.
14
+ 7. Click **Deploy** (for SimpleToken enter the initial supply first, e.g. 1000).
15
+ 8. A MetaMask popup appears. Review the gas fee and click **Confirm**.
16
+ 9. Wait for confirmation. In the Remix console copy the **transaction hash** and, under Deployed Contracts, the **contract address**.
17
+ 10. Open https://sepolia.etherscan.io and paste the transaction hash.
18
+ 11. Verify and note: Status = **Success**, Block number, From (your wallet), To (Contract Creation), Gas used, Transaction fee, Timestamp.
19
+ 12. Search the contract address on Etherscan to see the deployed contract.
20
+ 13. Optional: call a function (e.g. `setMessage` or `transfer`) from Remix and verify that transaction on Etherscan as well.
21
+
22
+ **Code file:** `HelloWorld.sol` (and `SimpleToken.sol` as an alternative)
23
+
24
+ ## Viva questions
25
+ - What is a testnet and why use it?
26
+ - What is gas?
27
+ - What is the difference between Remix VM and Injected Provider?
28
+ - What does Etherscan show for a transaction?